複制控制這一節需要注意的地方不多,主要有以下幾點:
1、定義自己的複制構造函數
什麼時候需要定義自己的複制構造函數,而不用系統提供的,主要遵循以下的經驗說明:
某些類必須對複制對象時發生的事情加以控制,這樣的類(1)經常有一個資料成員是指針,(2)有成員在構造函數中配置設定的其他資源;
而另一些類在建立對象時必須做一些特定的工作。
2、禁止複制
有些類是需要禁止複制的,如iostream類就不允許複制,但編譯器始終都會預設合成一個,但還是有辦法的:
為了防止複制,類隻要顯示聲明其複制構造函數為private就行了。
然而,這樣,類的友元和成員仍可以進行複制,如果想要連友元和成員中的複制也禁止,就可以聲明一個private複制構造函數但不對其定義,這是合法的。
3、析構函數的異同
與複制構造函數或指派操作符不同,編譯器總是會為我們合成一個析構函數,合成析構函數按照對象建立時的逆序撤銷每個非static成員。
析構函數與複制構造函數或指派操作符之間的一個重要差別是:即使我們編寫了自己的析構函數,合成析構函數仍然會運作。合成析構函數。
4、智能指針
智能指針是由于在有指針成員的類中,指針所指向的對象是共享的,防止出現懸垂指針而提出的一種管理指針的辦法。
為了闡述智能指針,我們來看一個例子:
如上一個類,如果我像這樣調用:
int obj = 0;
HasPtr ptr1(&obj, 42);
HasPtr ptr2(ptr1);
ptr1和ptr2的值相同,改變任意一個的值都可以改變其共享對象的值。
再看,可能出現懸垂指針的情況:
int *ip = new int(42);
HasPtr ptr(ip, 10);
delete ip;
ptr.set_ptr_val(0); //Disaster!!!
這裡ip和ptr中的指針指向了同一對象,删除了該對象時,ptr中的指針不再指向有效對象,但是你又不知道該對象不在了,是以,這樣就出現了懸垂指針。
是以,定義智能指針能有效地解決這個問題,為了避免多個指針共享一個對象時撤銷出現的懸垂指針問題,定義智能指針類的主要功能就是來保證在撤銷指向對象的最後一個指針時才删除該對象。
為了統計指向共享對象的指針的數量,引入使用計數,用其跟蹤該類有多少個對象共享同一指針,但使用計數為0時,删除對象。在設計上,将使用計數設計成一個單獨的類,用來封裝使用計數和相關指針。
如下:
引用上面的那個類,不同的是,讓HasPtr類儲存一個指向U_Ptr對象的指針,U_Ptr對象再指向實際的int基礎對象。如下:
其中,紅色部分是改動過的。指派操作符像下面這樣:
還有一種方法是定義值類型:
這種思路很簡單,就是給指針成員提供值語義,複制值型對象時,會得到一個不同的新副本,對副本所做的改變不會反映在原有對象上。如下,可以對指派操作符做點改變: