1. 程式人生 > 其它 >C++編譯器自動生成的函式

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。這裡要說明的是移動建構函式和移動賦值運算子。

  1. 移動建構函式和移動賦值運算子過載函式不會隱式宣告,必須自己定義。

  2. 如果使用者自定義了拷貝建構函式或者移動建構函式,那麼預設的建構函式將不會隱式定義,如果使用者需要,也需要顯式的定義。

  3. 移動建構函式不會覆蓋隱式的拷貝建構函式。

  4. 移動賦值運算子過載函式不會覆蓋隱式的賦值運算子過載函式。