我們已經知道在C++中,對象變量直接存儲的是對象的值。這是與Java不同的,在Java中對象變量存儲的是一個地址,該地址指向對象值實際存儲的地方。有時在C++中也需要實現這樣的布置,這就用到了指針pointer。在 C++中,一個指向對象的變量叫做指針。如果T是一種數據類型,則 T* 是指向這種數據類型的指針。 這裡重點介紹C++與Java的不同,要詳細了解C++中指針的使用
就像 Java中一樣,一個指針變量可以被初始化為空值 NULL,另外一個指針變量的值,或者一個調用new生成的新對象:
Employee* p = NULL;
Employee* q = new Employee("Hacker, Harry", 35000);
Employee* r = q;
實際上在C++中還有第四種可能,那就是指針可以被初始化為另外一個對象的地址,這需要使用地址操作符 & :
Employee boss("Morris, Melinda", 83000);
Employee* s = &boss;
這實際上並不是什麼好主意。保險的做法還是應該直接讓指針指向使用 new生成的新對象。
到目前為止,C++ 指針看起來非常像 Java 的對象變量。然而,這裡有一個很重要的語法的不同。我們必須使用星號操作符 * 來訪問指針指向的對象。如果 p 是一個指向Employee對象的指針,則 *p 才代表了這個對象:
Employee* p = . . .;
Employee boss = *p;
當我們需要執行對象的函數或訪問對象的一個數據域時,也需要使用 *p :
(*p).setSalary(91000);
*p外面的括號是必需的,因為 . 操作符比 * 操作符有更高的優先級。C的設計者覺得這種寫法很難看,所以他們提供了另外一種替代的寫法,使用 -> 操作符來實現 * 和 . 操作符的組合功能。表達式
p->setSalary(91000);
可以調用對象*p的函數 setSalary 。你可以簡單的記住 . 操作符是在對象上使用的,-> 操作符是在指針上使用的。
如果你不初始化一個指針,或者如果一個指針為空值 NULL 或指向的對象不再存在,則在它上面使用 * 或 -> 操作符就會出錯。 不幸的是 C++ runtime 系統並不檢查這個出錯。如果你范了這個錯誤,你的程序可能會行為古怪或死機。
而在Java中,這些錯誤是不會發生的。所有的reference都必須初始化,所有的對象只要仍有reference指向它就不會被從內存中清除,因此你也不會有一個指向已被刪除的對象的reference。Java的runtime 系統會檢查reference是否為空,並在遇到空指針時拋出一個null pointer的例外(exception)。
C++ 和 Java還有一個顯著的不同,就是 Java 有垃圾回收功能,能夠自動回收被廢棄的對象。而在C++中,需要程序員自己管理內存分配回收。
C++中當對象變量超出范圍時可以自動被回收。但是使用new生成的對象必須用delete操作符手動刪除,例如:
Employee* p = new Employee("Hacker, Harry", 38000);
. . .
delete p; /* 不在需要這個對象 */
如果你忘記刪除一個對象,那麼你的程序有可能最終用光所有內存。這就是我們常說的內存洩漏 (memory leak)。更重要的是,如果你如果刪除了一個對象,然後又繼續使用它,你可能覆蓋不屬於你的數據。如果你剛巧覆蓋了用於處理內存回收的數據域,那麼內存分配機制就可能運轉失常而造成更嚴重的錯誤,而且很難診斷和修復。因此,在C++中最好盡量少用指針。