天天看點

拷貝構造函數及其參數類型

拷貝構造函數的參數類型必須是引用,而且通常情況下還是const的,但是const并不是嚴格必須的。

#include <iostream>

#include <string>

using namespace std;

class CClass

{

public:

         CClass() : a(1), b("Hello, world.")

         {

         }

         // 拷貝構造函數,參數中的const不是嚴格必須的,但引用符号是必須的

         CClass(const CClass& c_class)

         {

                   a = c_class.a;

                   b = c_class.b;

         }

         void setValues(int a, string b)

         {

                   this->a = a;

                   this->b = b;

         }

         void printValues()

         {

                   cout << "a = " << a << endl;

                   cout << "b = " << b << endl;

         }

private:

         int a;

         string b;

};

int main(void)

{

         CClass c;

         c.setValues(100, "Hello, boys!");

         CClass d(c);              // 此處調用拷貝構造函數

         d.printValues();

         return 0;

}

如果将拷貝構造函數中的引用符号去掉&,編譯将無法通過,出錯的資訊如下:

非法的複制構造函數: 第一個參數不應是“CClass”

沒有可用的複制構造函數或複制構造函數聲明為“explicit”

原因:

如果拷貝構造函數中的參數不是一個引用,即形如CClass(const CClass c_class),那麼就相當于采用了傳值的方式(pass-by-value),而傳值的方式會調用該類的拷貝構造函數,進而造成無窮遞歸地調用拷貝構造函數。是以拷貝構造函數的參數必須是一個引用。

需要澄清的是,傳指針其實也是傳值,如果上面的拷貝構造函數寫成CClass(const CClass* c_class),也是不行的。事實上,隻有傳引用不是傳值外,其他所有的傳遞方式都是傳值。

附帶說明,在下面幾種情況下會調用拷貝構造函數:

a.       顯式或隐式地用同類型的一個對象來初始化另外一個對象。如上例中,用對象c初始化d;

b.       作為實參(argument)傳遞給一個函數。如CClass(const CClass c_class)中,就會調用CClass的拷貝構造函數;

c.        在函數體内傳回一個對象時,也會調用傳回值類型的拷貝構造函數;

d.       初始化序列容器中的元素時。比如 vector<string> svec(5),string的預設構造函數和拷貝構造函數都會被調用;

e.       用清單的方式初始化數組元素時。string a[] = {string(“hello”), string(“world”)}; 會調用string的拷貝構造函數。

如果在沒有顯式聲明構造函數的情況下,編譯器都會為一個類合成一個預設的構造函數。如果在一個類中聲明了一個構造函數,那麼就會阻止編譯器為該類合成預設的構造函數。和構造函數不同的是,即便定義了其他構造函數(但沒有定義拷貝構造函數),編譯器總是會為我們合成一個拷貝構造函數。

如果想阻止拷貝構造函數發生作用,那麼一個類,必須顯式聲明其拷貝構造函數,并且将其設為private, 并且其實作體是空的。因為僅僅是private的話,友元函數或者友元類還是有機會調用到這個拷貝構造函數。

通常情況下,如果一個類實作了拷貝構造函數,那麼這個類也需要實作預設構造函數。