C++拷貝構造函數中的陷阱
轉自微信公眾號:CPP開發前沿
拷貝構造函數大傢都比較熟悉,通俗講就是傳入一個對象,拷貝一份副本。
不過看似簡單的東西,實際不註意的話就會產生問題!
#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
)。
對於比較復雜的類型,編譯器就會自動生成一個缺省的拷貝構造函數。
class CExample { int a,b,c; };
這就是一個簡單結構的類,位拷貝,就是按對象在內存中的二進制進行拷貝,對於不涉及指針等類型的時候,位拷貝是比較不錯的拷貝方法。
但是,要是一個類中有指針類型的時候,如:
class CExample { int a,b,c; char *str; };
位拷貝就會把指針地址拷貝瞭一下,話句話說,這裡隻進行瞭“淺拷貝”,一旦副本裡涉及到指針的操作,必然就會影響到原始對象的成員變量,這就是導致,上面代碼中對象的整數變量沒被修改(對整數變量的位拷貝其實就是一種“深拷貝”),而str所指的對象被修改的原因。
那麼該如何防止對副本的修改影響原始對象呢?
答案是用戶自定義拷貝構造函數!
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純開源 指出的三法則:
三法則(英語:rule of three,the Law of The Big Three,The Big Three;三法則,三大定律)在 C++ 程序設計裡,它是一個以設計的基本原則而制定的定律,三法則的要求在於,假如類型有明顯地定義下列其中一個成員函數,那麼程序員必須連其他二個成員函數也一同編寫至類型內,亦即下列三個成員函數缺一不可。
析構函數(Destructor
)
復制構造函數(copy constructor
)
復制賦值運算符(copy assignment operator
)
到此這篇關於C++拷貝構造函數中的陷阱的文章就介紹到這瞭,更多相關拷貝構造函數陷阱內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!