在c++中,當類中有指針類型的資料成員時,必須注意在構造函數中,配置設定專門的存儲單元,并将位址指派給指針型資料成員。
這樣做的目的在于,要保證指針指向的存儲單元能夠由類本身控制。
如果這種情形處理不好,将可能會造成災難性的後果,盡管多數情況程式看上去執行還算正常(這種錯誤是真正可怕的錯誤)。
為了幫助讀者了解,本文将從執行個體出發,展示不用這種處理的災難性後果,同時給出正确處理的方法示範。
一、一個編譯正确,運作也正确的壞程式
這個程式在執行main()函數時,第31行利用定義好的 x 數組,建立了arr 對象。第33行arr.showarray();輸出的結果表明,對象的建立是正确的。
然而,這的确是個正确的壞程式。大多數情況不會出問題。但是,有時,無法預料到是何時,運作結果可能會不正确;甚至,有其他意外。
這不是無中生有,危言聳聽。
讓我們逐漸接近内幕。
二、讓面向對象的機制失效的程式
【運作結果】
1 2 3 4 5
1 2 3 999 5
請按任意鍵繼續. . .
【一點說明】
其實還是上面的程式,隻在main()中多加了兩個語句。結果,在沒有對 arr 對象做任何操作的前提下,arr 的值卻變了!對象的封裝性何在?!對象值的改變沒有通過類的内部操作完成,也不是通過調用公共接口完成。而是,在arr 沒有參與的情況下,變化已經發生。明明你買了一隻烤鴨放在自家的冰箱裡,取出來的卻是一坨nf!
更為嚴重的是,例程2中甚至将showarray成員聲明為const成員函數(第9和24行),将arr對象聲明為const對象(第35行)。常對象不允許修改的底線也被挑戰了,且得逞了!
這還不是最嚴重的!
三、這個類會釀成災難
-17891602 -17891602 -17891602 -17891602 -17891602
【解釋】
在注釋中已經指出了災難所在,會得出錯誤的結果,災難甚至可能是程式停止執行,意外退出。也有可能輸出還會“正确”,而“正确”的惟一解釋是這段程式太短了,arr中的arr_point指向的空間恰好還沒有被作業系統配置設定作其他用途。當例程3的第12行和第13行中間插入了其他代碼,完成了一些操作,甚至轉移過流程,誰也說不清到執行第13行時,原先x曾經占用的記憶體的作用。的确,arr中的arr_point指向的是一個誰都說不清楚正在作何用的空間!!這個例子所示的隻是顯式地、有意地讓災難發生。在實際的項目中,類似 delete
[ ] x; 的操作可能不在這裡發生,可能根本不是由于delete造成。樂觀些想這個問題, 如果在災難發生前我們覺察出了問題,要在幾萬行代碼中找到問題的根源,也是一件相當不易的事情,需要會出巨大的成本。
而這一切,如果能遵循本文開頭的囑咐,原來是不會發生的。
四、深刻了解:錯誤是這樣發生的
用例程1來說明問題。執行例程1時,發生的主要事情如圖所示:

是以,在例程2中,main()函數可以修改 x[3] 的值;例程3中,x 數組已經被釋放了,arr 對象仍然“一往無前”地将之用作數組。後一種情況是災難性的,前 種情況也千萬不要将之用作為技巧:看,我能夠繞開c++的限制修改對象成員指向的值(有些hacker的感覺?)。在工程中,切忌将不同實體間的聯系複雜化,這是一種複雜化的表現,多種機制瞎攪乎的結果,必定是品質低下、破綻百出、bug多多的程式。
五、正确的做法
程式的關鍵是intarray類的構造函數和析構函數。在構造函數中,為arr_point指向的空間專門配置設定存儲單元并指派,進而這塊存儲區域成為相應對象的專屬操作對象,不通過面向對象的機制,不能通路這兒的空間。盡管在main()函數中涉及的 x 數組的值修改,甚至釋放 x 所占的空間,但此時,x 和arr 對象已經完全沒有任何關系,對arr_point 所指向的空間沒有任何的影響。程式中的各實體之間的“耦合”達到最小,各自按照各自的機制運作。
下面的圖示進一步說明了例程中記憶體空間的變化。
六、補充一個例子:當指針指向字元時
【說明】
七、總結
重申本文中心:在c++中,當類中有指針類型的資料成員時,必須注意在構造函數中,配置設定專門的存儲單元,并将位址指派給指針型資料成員。