摘自《C++ Primer Plus》第6版 12.1.2和12.1.3ios
淺拷貝只是對指針的拷貝,拷貝後兩個指針指向同一個內存空間,深拷貝不但對指針進行拷貝,並且對指針指向的內容進行拷貝,經深拷貝後的指針是指向兩個不一樣地址的指針。c++
c++ primer plus裏深淺拷貝的圖示:數據結構
逐個複製成員函數
深度複製this
示例:spa
#include <iostream> using namespace std; class StringBad { private: char * str; int len; static int num_strings; public: StringBad(const char * s); StringBad(); ~StringBad(); friend ostream & operator<<(ostream & os, const StringBad &st); }; int StringBad::num_strings = 0; StringBad::StringBad(const char * s) { len=strlen(s); str=new char(len+1); strcpy(str,s); num_strings++; cout << num_strings << ": /"" << str << "/" object created/n"; } StringBad::StringBad() { len=4; str=new char[4]; strcpy(str,"C++"); num_strings++; cout << num_strings << "; /"" << str << "/" object created/n"; } StringBad::~StringBad() { cout << "/"" << str << "/" object deleted, "; --num_strings; cout << num_strings << " left/n"; delete [] str; } ostream & operator << (ostream &os, const StringBad &st) { os << st.str; return os; } void callme1(StringBad &); void callme2(StringBad); int main() { StringBad headline1("headline1"); StringBad headline2("headline2"); StringBad sports("sports"); cout << "headline1: " << headline1 << endl; cout << "headline2: " << headline2 << endl; cout << "sports: " << sports << endl; callme1(headline1); cout << "headline1: " << headline1 << endl; callme2(headline2); cout << "headline2: " << headline2 << endl; cout << "Initialize one object to another: /n"; StringBad sailor = sports; cout << "sailor: " << sailor; cout << "Assign one object to another: /n"; StringBad knot; knot=headline1; cout << "knot: " << knot << endl; cout << "End of main" << endl; return 0; } void callme1(StringBad & rsb) { cout << "String passed by reference: /n"; cout << " /" " << rsb << " /"" << endl; } void callme2(StringBad sb) { cout << "String passed by value: /n"; cout << " /" " << sb << " /"" << endl; }
若是定義了構造函數,C++將不會生成默認構造函數。不然,編譯器將提供這樣的默認構造函數:指針
StringBad::StringBad(){}
拷貝構造函數的原型是code
StringBad::StringBad(const StringBad &)
什麼時候調用拷貝構造函數:對象
** 新建一個對象並將其初使化爲同類對象,如
StringBad aa(headline1);
StringBad bb=headline1;
StringBad cc=StringBad(headline1);
StringBad * pdd=new StringBad(headline1);
其中,StringBad bb=headline1;
StringBad cc=StringBad(headline1);
可能會使用拷貝構造函數直接建立對象bb和cc,也有可能使用拷貝構造函數先生成一個臨時對象,而後將臨時對象的內容賦給bb和cc,這取決於具體的實現。
StringBad * pdd=new StringBad(headline1);會初使化一個匿名對象,並將新對象的地址賦給pdd指針內存
賦值操做符:
ANSI C 容許結構體賦值,C++容許對象賦值,經過自動重載賦值操做符實現的。
StringBad & StringBad::operator =(const StringBad &)
將已有的對象賦給另外一個對象時,將使用重載的賦值操做符。
StringBad kk("just a test"); StringBad aa; aa=kk;
但初使化對象的時候,使用的是拷貝構造函數
StringBad & StringBad::operator=(const StringBad &st) { if(this==&st) return *this; delete [] str; len=st.len; str=new char[len+1]; strcpy(str,st.str); retern *this; }
#include <iostream> using namespace std; class StringGood { private: char * str; int len; static int num_strings; public: StringGood(const char * s); StringGood(const StringGood & st); StringGood(); ~StringGood(); friend ostream & operator<<(ostream & os, const StringGood &st); StringGood & operator = (const StringGood &); }; int StringGood::num_strings = 0; StringGood::StringGood(const char * s) { len=strlen(s); str=new char(len+1); strcpy(str,s); num_strings++; cout << num_strings << ": /"" << str << "/" object created/n"; } StringGood::StringGood(const StringGood & st) { len=st.len; str=new char[len+1]; strcpy(str,st.str); num_strings++; cout << num_strings << ": /"" << str << "/" object created/n"; } StringGood::StringGood() { len=4; str=new char[4]; strcpy(str,"C++"); num_strings++; cout << num_strings << "; /"" << str << "/" object created/n"; } StringGood::~StringGood() { cout << "/"" << str << "/" object deleted, "; --num_strings; cout << num_strings << " left/n"; delete [] str; } ostream & operator << (ostream &os, const StringGood &st) { os << st.str; return os; } StringGood & StringGood::operator = (const StringGood &st) { if(this==&st) return *this; delete[] str; len=st.len; str=new char[len+1]; strcpy(str,st.str); return *this; } void callme1(StringGood &); void callme2(StringGood); int main() { StringGood headline1("headline1"); StringGood headline2("headline2"); StringGood sports("sports"); cout << "headline1: " << headline1 << endl; cout << "headline2: " << headline2 << endl; cout << "sports: " << sports << endl; callme1(headline1); cout << "headline1: " << headline1 << endl; callme2(headline2); cout << "headline2: " << headline2 << endl; cout << "Initialize one object to another: /n"; StringGood sailor = sports; cout << "sailor: " << sailor << endl; cout << "Assign one object to another: /n"; StringGood knot; knot=headline1; cout << "knot: " << knot << endl; cout << "End of main" << endl; return 0; } void callme1(StringGood & rsb) { cout << "String passed by reference: /n"; cout << " /" " << rsb << " /"" << endl; } void callme2(StringGood sb) { cout << "String passed by value: /n"; cout << " /" " << sb << " /"" << endl; }
1. 只要類裏有指針時,就要寫本身版本的拷貝構造函數和賦值操做符函數。這兩個函數中能夠拷貝那些被指向的數據結構,從而使每一個對象都有本身的拷貝;或者能夠採用某種引用計數機制,去跟蹤當前有多少個對象指向某個數據結構。(也能夠靜態計數對象的個數,好比c++ primer plus第六版的例子,static int num_strings)
2. 某些類在實現拷貝構造函數和賦值操做符時,能夠確信程序中不會作拷貝和賦值操做的時候,去實現它們會得不償失,所以能夠:只聲明這些函數(聲明爲private成員)而不去定義實現它們。這就防止了會有人去調用它們,也防止了編譯器去生成它們。
3. 淺拷貝在構造和析構對象時容易產生問題,如無必要儘可能不用淺拷貝。當咱們要傳遞複雜結構的信息,而又不想產生另外一份數據時,可使用淺拷貝,好比引用傳參。
4. 智能指針是淺拷貝概念的加強。好比智能指針能夠維護一個引用計數來代表指向某塊內存的指針數量,只有當引用計數減至0時,才真正釋放內存。