天天看點

C++防災——為指針成員配置設定專門的存儲空間

  在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時,發生的主要事情如圖所示:

C++防災——為指針成員配置設定專門的存儲空間

  是以,在例程2中,main()函數可以修改 x[3] 的值;例程3中,x 數組已經被釋放了,arr 對象仍然“一往無前”地将之用作數組。後一種情況是災難性的,前 種情況也千萬不要将之用作為技巧:看,我能夠繞開c++的限制修改對象成員指向的值(有些hacker的感覺?)。在工程中,切忌将不同實體間的聯系複雜化,這是一種複雜化的表現,多種機制瞎攪乎的結果,必定是品質低下、破綻百出、bug多多的程式。

  五、正确的做法

  程式的關鍵是intarray類的構造函數和析構函數。在構造函數中,為arr_point指向的空間專門配置設定存儲單元并指派,進而這塊存儲區域成為相應對象的專屬操作對象,不通過面向對象的機制,不能通路這兒的空間。盡管在main()函數中涉及的 x 數組的值修改,甚至釋放 x 所占的空間,但此時,x 和arr 對象已經完全沒有任何關系,對arr_point 所指向的空間沒有任何的影響。程式中的各實體之間的“耦合”達到最小,各自按照各自的機制運作。

  下面的圖示進一步說明了例程中記憶體空間的變化。

C++防災——為指針成員配置設定專門的存儲空間

  六、補充一個例子:當指針指向字元時

【說明】

  

  七、總結

  重申本文中心:在c++中,當類中有指針類型的資料成員時,必須注意在構造函數中,配置設定專門的存儲單元,并将位址指派給指針型資料成員。

繼續閱讀