C++編譯器自動生成的函式
https://www.programminghunter.com/article/4569180387/
在C++中當建立一個空類時,C++就會預設的為這個類建立4個函式:預設的建構函式、解構函式、拷貝建構函式、以及賦值操作符。本文參考Effective C++介紹這幾個函式。
1.函式的原型以及函式建立的時機
C++中建立一個空類:
class Empty {};
預設會生成4個函式,其函式的原型如下:
public: Empty() { ... } Empty(const Empty& rhs) { ... } ~Empty() { ... } Empty& operator=(const Empty& rhs) { ... }
說明:1) 這些函式只有在需要呼叫的時候,編譯器才會生成。2) 4個函式都是public的。3) 4個函式都是inline的(即函式定義在類的定義中的函式)。4) 如果你顯式的聲明瞭這些函式中的任何一個函式,那麼編譯器將不再生成預設的函式。
比如,當遇到下列語句時,函式會被編譯器生成:
Empty e1; //預設建構函式
//物件銷燬時,解構函式
Empty e2(e1); //拷貝建構函式
e2 = e1; //賦值運算子
另外,還存在兩種預設的函式:就是取地址運算子和取地址運算子的const版本,這兩個函式在《Effective C++》中沒有提及。
public:
Empty* operator&() { ... }
const Empty* operator&() const { ... }
這兩個函式是確實存在的,正如下面的程式碼可以正常工作:
#include <stdio.h> class Empty { }; int main(int argc, char** argv) { Empty a; const Empty *b = &a; printf("%p\n", &a); //呼叫取地址運算子 printf("%p\n", b); //呼叫const取地址運算子 }
一個容易被忽略的問題:自定義的拷貝建構函式不僅會覆蓋預設的拷貝建構函式,也會覆蓋預設的建構函式。下面的程式碼是編譯不過的,使用者必須再顯式的定義一個無參的建構函式。
class Empty {
public:
Empty(const Empty& e) { } //拷貝建構函式
};
int main(int argc, char** argv)
{
Empty a;
}
2. 賦值操作符存在的問題
賦值操作符函式的行為與拷貝建構函式的行為基本是相同的,編譯器生成賦值操作符函式是有條件的,如果會產生無法完成的操作,編譯器將拒絕產生這一函式。那麼什麼時候編譯器無法完成賦值這一行為呢?考慮如下情形(來源Effective C++):
template<class T>
class NameObject {
public:
NameObject(std::string& name, const T& value);
private:
std::string& nameValue; //引用成員變數
const T objectValue; //const成員變數
};
然後考慮下面的語句會發生什麼事:
std::string newDog("abc");
std::string oldDog("xxx");
NameObject<int> p(newDog, 2);
NameObject<int> s(oldDog, 10);
p = s; //將會發生什麼?
賦值語句之前,p.nameValue指向newDog, s.nameValue指向oldDog。那麼賦值之後呢?p.nameValue應該指向s.nameValue指向的物件嗎?但是C++有一條規定:引用不能改指向另外一個物件。
對於變數objectValue,C++規定:更改const成員是不合法的。
因此如果上面兩種情形中的任何一種發生了,C++編譯器給出的響應是:拒絕編譯這一行的賦值動作。如果你這麼做了,C++編譯器會報錯。如果你執意要進行賦值操作,那麼可以自己定義一個賦值操作符過載函式。
3. c++0x中的新變化
C++0x中引入了“右值引用”和“移動語義”的概念,可以實現對右值的引用。(左值和右值的解釋可以見http://amyz.itpub.net/post/34151/411832)
移動語義,簡單來說,就是在一個右值物件的生命期結束之前,將其資源偷過來,為我所用。有關移動語義的詳細內容這裡不做詳述,大家可以參見csdn上一篇文章 http://blog.csdn.net/pongba/article/details/1684519。這裡要說明的是移動建構函式和移動賦值運算子。
-
移動建構函式和移動賦值運算子過載函式不會隱式宣告,必須自己定義。
-
如果使用者自定義了拷貝建構函式或者移動建構函式,那麼預設的建構函式將不會隱式定義,如果使用者需要,也需要顯式的定義。
-
移動建構函式不會覆蓋隱式的拷貝建構函式。
-
移動賦值運算子過載函式不會覆蓋隱式的賦值運算子過載函式。