深拷贝与浅拷贝、空类与空数组
一、深拷貝與淺拷貝
說得簡(jiǎn)單點(diǎn),假設(shè)一個(gè)類有指針成員,如果在拷貝的時(shí)候順帶連指針指向的內(nèi)存也分配了,就稱為深拷貝,如下圖(v2 從 v 拷貝而來):
如果只是分配指針本身的內(nèi)存,那就是淺拷貝,如下圖:
淺拷貝造成的問題是有兩個(gè)指針指向同塊內(nèi)存,delete 其中一個(gè)指針,那么剩下的指針將成為野指針。編譯器合成的默認(rèn)拷貝構(gòu)造函數(shù)和賦值運(yùn)算符是淺拷貝的,如果只是普通成員的賦值,淺拷貝也是可以的。
C++ Code?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | ? | #ifndef?_STRING_H_ #define?_STRING_H_ class?String { public: ????String(char?*str?=?""); ????~String(); ????String(const?String?&other); ????String?&operator=(const?String?&other); ????void?Display(); private: ????char?*AllocAndCpy(char?*str); ????char?*str_; }; #endif?//?_STRING_H_ |
| 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 | ? | #include?"String.h" //#include?<string.h> #include?<cstring> #include?<iostream> using?namespace?std; String::String(char?*str/*?=?*/) { ????str_?=?AllocAndCpy(str); } String::~String() { ????delete[]?str_; } String::String(const?String?&other) { ????str_?=?AllocAndCpy(other.str_); } String?&String::operator?=(const?String?&other) { ????if?(this?==?&other) ????????return?*this; ????delete[]?str_; ????str_?=?AllocAndCpy(other.str_); ????return?*this; } char?*String::AllocAndCpy(char?*str) { ????int?len?=?strlen(str)?+?1; ????char?*tmp?=?new?char[len]; ????memset(tmp,?0,?len); ????strcpy(tmp,?str); ????return?tmp; } void?String::Display() { ????cout?<<?str_?<<?endl; } |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | ? | #include?"String.h" int?main(void) { ????String?s1("AAA"); ????s1.Display(); ????String?s2?=?s1;?????//?調(diào)用拷貝構(gòu)造函數(shù) ????//?系統(tǒng)提供的默認(rèn)拷貝構(gòu)造函數(shù)實(shí)施的是淺拷貝?s2.str_?=?s1.str_ ????String?s3; ????s3.Display(); ????s3?=?s2;????????????//?調(diào)用等號(hào)運(yùn)算符 ????//?系統(tǒng)提供的默認(rèn)等號(hào)運(yùn)算符實(shí)施的是淺拷貝?s3.str_?=?s2.str_; ????//?s3.operator=(s2); s3.Display(); ????//?要讓對(duì)象是獨(dú)一無二的,我們要禁止拷貝 ????//?方法是將拷貝構(gòu)造函數(shù)與=運(yùn)算符聲明為私有,并且不提供它們的實(shí)現(xiàn) ????return?0; } |
上面程序中String 類有一個(gè)char* str_ 成員,故實(shí)現(xiàn)了深拷貝,這樣不會(huì)造成內(nèi)存被釋放兩次的錯(cuò)誤,或者修改指針指向的內(nèi)存會(huì)影響另一個(gè)對(duì)象的錯(cuò)誤。此外,如果我們想讓對(duì)象是獨(dú)一無二的,需要禁止拷貝,只需要將拷貝構(gòu)造函數(shù)和等號(hào)運(yùn)算符聲明為私有,并且不提供它們的實(shí)現(xiàn)。
注意:在編寫派生類的賦值函數(shù)時(shí),不要忘記對(duì)基類的數(shù)據(jù)成員重新賦值,可以通過調(diào)用基類的賦值函數(shù)來實(shí)現(xiàn),比如在
Derived& Derived::operator=(const Derived& other) { } 中調(diào)用Base::operator=(other);
注:盡量不要把 string 類型變量當(dāng)作結(jié)構(gòu)體成員,因?yàn)橛行┖瘮?shù)對(duì)整個(gè)結(jié)構(gòu)體進(jìn)行拷貝時(shí)默認(rèn)就是 memcpy,即對(duì)于 string 變量來說只拷貝了指針,這可能會(huì)造成一些莫名其妙的bug,比如指針地址被重用而導(dǎo)致指針指向的內(nèi)容可能被覆蓋重寫。--踩過的坑
二、空類與空數(shù)組
空類默認(rèn)產(chǎn)生的成員:
class Empty {};
Empty(); // 默認(rèn)構(gòu)造函數(shù) Empty( const Empty& ); // 默認(rèn)拷貝構(gòu)造函數(shù) ~Empty(); // 默認(rèn)析構(gòu)函數(shù) Empty& operator=( const Empty& ); ?// 默認(rèn)賦值運(yùn)算符 Empty* operator&(); ? ? ? ? ? ? ? // 取址運(yùn)算符 const Empty* operator&() const; ? ?// 取址運(yùn)算符 const
| 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 | ? | #include?<iostream> using?namespace?std; class?Empty { public: ????Empty?*operator&() ????{ ????????cout?<<?"AAAA"?<<?endl; ????????return?this; ????} ????const?Empty?*operator&()?const ????{ ????????cout?<<?"BBBB"?<<?endl; ????????return?this; ????} }; int?main(void) { ????Empty?e; ????Empty?*p?=?&e;??????//?等價(jià)于e.operator&(); ????const?Empty?e2; ????const?Empty?*p2?=?&e2; ????cout?<<?sizeof(Empty)?<<?endl; ????return?0; } |
單步調(diào)試一下,可以看到分別調(diào)用了兩個(gè)取地址運(yùn)算符函數(shù),而且空類的大小為1個(gè)字節(jié)。
體會(huì)一下下面的程序結(jié)果:
C++ Code?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | ? | #include<iostream> using?namespace?std; int?main() { ????int?a[0]; ????class?B?{}; ????struct?C ????{ ????????int??m; ????????int??n; ????????char?buffer[]; ????}; ????class?D ????{ ????????int??s[0]; ????}; ????cout?<<?"sizeof(a)="?<<?sizeof(a)?<<?endl;?//0 ????cout?<<?"B{}="?<<?sizeof(B)?<<?endl;?//1 ????cout?<<?"C="?<<?sizeof(C)?<<?endl;?//8 ????cout?<<?"D="?<<?sizeof(D)?<<?endl;?//0 ????return?0; } |
參考:
C++ primer 第四版
Effective C++ 3rd
C++編程規(guī)范
總結(jié)
以上是生活随笔為你收集整理的深拷贝与浅拷贝、空类与空数组的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ubuntu台式机如何用usb无线网卡共
- 下一篇: mybatis添加记录时返回主键id