了解C++默默編寫并調用哪些函數
當你建立一個類,但是如果你自己沒聲明其中一些東西,編譯器就會為它聲明(編譯器版本的)一個拷貝構造函數、一個 copy 操作符和一個析構函數。此外如果你沒有聲明任何構造函數,編譯器也會為你聲明一個 預設構造函數。所有這些函數都是 public且 inline。
例:當你寫下class Empty{ };
就相當于你寫下了:
Class Empty
{
public:
Empty() {...} // 預設構造函數
Empty(const Empty& rhs){...} //拷貝構造函數
~Empty(){...} //析構函數
Empty& operator=(const Empty& rhs){...} // copy 操作符.
};
當這些函數被調用的時候,它們才會被編譯器建立出來,如下代碼可以使上述每個函數均生成;
Empty e1; //預設構造函數(銷毀調用析構)
Empty e2(e1); //拷貝構造
e2 = e1; //拷貝構造符
如果你的類中寫了構造函數,但是沒有拷貝構造等,編譯器會自動給你建立拷貝構造等,但一般來說隻有代碼合法且可以證明其有意義的才會被生成。萬一這兩個條件中有一個不符合,則編譯器會拒絕生成operator=;
例:成員變量是一個引用
template<class T>
class Name
{
public:
Name(std: strings name, const T& value); //并未聲明 operator
private:
string& namevalue;
const T objectvalue;
};
現在考慮下面會發生什麼事?
string new("new");
string old("old");
Name<int>p(new,2);
Name<int>s(old,3);
p = s;
剛開始p.name和s.name都指向各自的string對象,當p=s之後,p.name是指向s.name的那個string嗎?
當然不是,因為C++不允許讓引用改指向不同的對象。是以最好的方法就是自己定義。
如何拒絕讓編譯器自動生成
假設有有一個葉子類:class Leaf {...}
世界上沒有兩片完全相同的葉子,是以我們認為,為Leaf的對象進行拷貝是沒有意義的,因為你無法複制一個獨一無二的東西,是以你想讓以下代碼無法編譯通過:
Leaf L1;
Leaf L2;
Leaf L3(L1); //企圖拷貝L1
L1 = L2; //企圖拷貝L2;
通常如果你不希望 class支援某特定功能,隻要不聲明對應函數就是了。但這個政策對copy構造函數和copy操作符卻不起作用,因為上面已經說明,如果你不聲明它們,當嘗試調用它們時,編譯器會為你聲明它們。
這把你逼到了一個困境。如果你不聲明copy構造函數或 copy操作符,編譯器可能為你産出一份,于是你的class支援copy,如果你聲明它們,你的 class還是支援 copy,但你的目的卻是要阻止 copy。
一個地方可以入手,因為所有編譯器生成的函數都是 public.為阻止這些函數被建立出來你得自行聲明它們,但這裡并沒有什麼需求使你必須将它們聲明為 public.是以你可以将copy構造函數或 copy操作符聲明為 private.明确的聲明一個成員函數來阻止編譯器暗自建立其專屬版本,并且令這些函數為 private,阻止調用它。
一般而言這個做法也并不絕對安全,因為 member函數和 friend函數還是可以調用你的 private函數。除非你夠聰明,不去定義它們,那麼如果某些人不慎調用任何一個,會獲得一個連接配接錯誤( linkage error)。
實作方式:
class Leaf
{
public:
...
private:
Leaf(const Leaf& ); //參數名稱并非必要,隻不過大家習慣寫出來
Leaf& operator=(const Leaf& ); //隻聲明
};
其他做法:
将連接配接期錯誤移至編譯期是可能的(而且那是好事,畢竟越早偵測出錯誤越好),隻要将copy構造函數和 copy操作符聲明為 private就可以辦到,但不是在 Leaf自身,而是在一個專門為了阻止 copying動作而設計的 base class,這個 base class 非常簡單:
class Uncopyable
{
protected: //允許對象構造和析構
Uncopyable() { }
~Uncopyable() { }
private:
Uncopyable(const Uncopyable& ); //但阻止 copy
Uncopyable& operator=(const Uncopyable& );
};
為求阻止Leaf對象被拷貝,我們可以做的就是繼承 Uncopyable:
class Leaf: private Uncopyable {...};
//class不再聲明copy構造函數或copy操作符
總結:
編譯器可以暗自為 class 建立 default構造函數、copy構造函數、copy操作符,以及析構函數。
為阻止編譯器自動提供的功能,可将相應的成員函數聲明為 private并且不予實作。或使用像 Uncopyable這樣的 base class也是一種做法;