拷貝建構函式大家都比較熟悉,通俗講就是傳入一個物件,拷貝一份副本。
不過看似簡單的東西,實際不注意的話就會產生問題!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
#include<iostream> using namespace std; class CExample { public: int a,b,c; char *str; public: //建構函式 CExample(int tb) { a = tb; b = tb+1; c = tb+2; str=(char *)malloc(sizeof(char)*10); strcpy(str,"123456789"); cout<<"creat: "<<endl; } //解構函式 ~CExample() { cout<< "delete: "<<endl; } void Show () { cout<<a<<endl; } //拷貝構造 //CExample(const CExample& C) //{ // str=(char *)malloc(sizeof(char)*10); // strcpy(str,C.str); // cout<<"copy"<<endl; //} }; //全域性函式,傳入的是物件 void g_Fun(CExample C) { C.a=0;C.b=0;C.b=0; strcpy(C.str,"aaabbbccc"); cout<<"test"<<endl; } int main() { CExample test(1); cout<<"str:"<<test.str<<" a="<<test.a<<" b="<<test.b<<" c="<<test.c<<endl; g_Fun(test);//傳入物件 cout<<"str:"<<test.str<<" a="<<test.a<<" b="<<test.b<<" c="<<test.c<<endl; getchar(); return 0; } |
這個結果似乎出乎了我們的預料,作為形式引數 test物件被修改了,同時是test.str的部分被修改了,test的整數成員變數沒有被修改!
我們們先了解一下系統預設的拷貝建構函式,因為類中沒有寫自己的拷貝建構函式,所以呼叫的是預設的拷貝建構函式。
Thinking in c++:對於簡單結構,編譯器會自動生成一個預設的,就是位拷貝(bitcopy)。
對於比較複雜的型別,編譯器就會自動生成一個預設的拷貝建構函式。
1 2 3 4 |
class CExample { int a,b,c; }; |
這就是一個簡單結構的類,位拷貝,就是按物件在記憶體中的二進位制進行拷貝,對於不涉及指標等型別的時候,位拷貝是比較不錯的拷貝方法。
但是,要是一個類中有指標型別的時候,如
1 2 3 4 5 |
class CExample { int a,b,c; char *str; }; |
位拷貝就會把指標地址拷貝了一下,話句話說,這裡只進行了“淺拷貝”,一旦副本里涉及到指標的操作,必然就會影響到原始物件的成員變數,這就是導致,上面程式碼中物件的整數變數沒被修改(對整數變數的位拷貝其實就是一種“深拷貝”),而str所指的物件被修改的原因。
那麼該如何防止對副本的修改影響原始物件呢?
答案是使用者自定義拷貝建構函式!
1 2 3 4 5 6 7 |
CExample(const CExample& C) { a=C.a;b=C.b;c=C.b; str=(char *)malloc(sizeof(char)*10); strcpy(str,C.str); cout<<"copy"<<endl; } |
這樣就可以正確完成拷貝構造的操作了。
總結:對於簡單的資料型別,可以使用系統預設的拷貝建構函式;但對於複雜的資料型別(如指標),其實就是深拷貝和淺拷貝的區別!一般類如果包含指標或引用成員,應該遵守Rule of Three原則。(感謝@24K純開源)
@24K純開源 指出的三法則:
三法則(英語:rule of three,the Law of The Big Three,The Big Three;三法則,三大定律)在 C++ 程式設計裡,它是一個以設計的基本原則而制定的定律,三法則的要求在於,假如型別有明顯地定義下列其中一個成員函式,那麼程式設計師必須連其他二個成員函式也一同編寫至型別內,亦即下列三個成員函式缺一不可。 [1]: