在C++98中,有四種特殊成員函數,分別爲:
1默認構造函數
2默認析構函數
3默認拷貝構造函數
4默認拷貝賦值函數
上面這4個特殊成員函數有如下特點:
1只在你沒有在類的定義中顯式聲明他們的時候,C++纔會去創建他們。
2他們默認是inline類型的
3他們是non-virtual的,除非這個成員函數是析構函數,並且這個類的父類的析構函數是virtual的,這時候,c++會爲這個子類產生一個virtual類型的析構函數。
在C++11中,新增了兩個特殊成員函數:移動構造函數和移動賦值運算符:
class Widget {
public:
Widget(Widget&& rhs); // move constructor
Widget& operator=(Widget&& rhs); //move assignment operator
};
Move操作是類成員級別的move,也就是說move是將類中的成員逐個地move,由於並不是類中的所有成員都支持move操作,所以,move是部分move。
如果類成員不支持move操作(如某個對象成員沒有定義move構造函數,C++98標準下的類就沒有move函數,所以也就不支持move操作),這時,會調用這些類的拷貝構造函數。如果是普通的成員變量,則會直接複製這個成員變量。
注意:
1這兩個move函數是相互關聯的,只有當你沒有顯式聲明他們當中任何一個時,c++纔會自動生成這些函數,如果你聲明瞭其中任何一個move函數,那麼這兩個move函數都不會由c++自動生成了。
2 當你顯式聲明瞭拷貝構造函數或者拷貝賦值函數中的任何一個時,C++都不會再生成move函數了,當我們顯式聲明瞭一個拷貝函數時,這就表明通常的拷貝對象方式(逐個數據成員的拷貝)已經不適合這個類了,move操作是“數據成員”級的move,編譯器認爲如果“成員級”的拷貝不適合這個類的拷貝操作,那麼“成員級”的move也不適合這個類。
3反過來說,當你聲明瞭兩個move函數中的任何一個,那麼編譯器也就不會再爲這個類生成拷貝函數了。
4如果一個類中有顯式聲明的析構函數,C++將不會再爲其產生move函數。
Rule of Three: 當你聲明瞭“拷貝構造函數”,“拷貝賦值函數”,“析構函數”中的任意一個時,你也應當聲明另外的兩個。原因:一個類需要顯式自定義的拷貝構造函數,多是因爲它要管理資源,那麼,拷貝賦值函數以及析構函數自然也就被牽扯到資源管理中來了。
總結:只有下面三個條件都滿足時,move函數纔會被C++自動生成:
1沒有顯式聲明copy函數(拷貝構造和拷貝賦值)
2沒有聲明任何move函數(move構造函數和move賦值函數)
3沒有顯式聲明析構函數
C++11 對於特殊函數的生成規則
1 默認構造函數:規則與C++98相同,只有當類中沒有顯式聲明構造函數時,C++纔會自動爲其生成一個無參的默認構造函數。
2析構函數:與C++98相同,只有當基類的析構函數是virtual時,子類的析構函數纔會是virtual的。
3拷貝構造函數:運行時行爲與C++98相同,成員級拷貝,即逐個會拷貝類的non-static數據成員。只有當類中沒有顯式聲明拷貝構造函數和move函數時,C++纔會自動生成。爲一個有顯式拷貝賦值函數或者析構函數的類自動生成拷貝構造是被阻止的。
4拷貝賦值運算符:運行時行爲與C++98相同,成員級拷貝,只有當類中沒有顯式聲明的拷貝構造函數或者任何move函數時,纔會自動生成。爲一個具有顯式拷貝構造函數或者析構函數的類自動生成拷貝賦值函數是被阻止的。
5move構造函數和move賦值運算符:成員級move,即它會逐個move類中的non-static數據成員。只有類中不包含顯式的拷貝函數,move函數,析構函數時纔會產生。
6模板成員函數的存在並不會阻止C++產生特殊成員函數,比如,在如下代碼中:
class Widget {
template<typename T> //copy-construct Widget
Widget(const T& rhs); //from anything
template<typename T> //copy-assign Widget
Widget& operator=(const T& rhs); //from anything
};
編譯器仍然會自動生成拷貝函數和move函數,即使當T爲Widget時,模板會實例化爲拷貝函數。