回覆列表
-
1 # 胖胖的小老鼠
-
2 # 程式設計老大叔
關於C++物件的複製,大部分時間我們用的都是淺複製,比如賦值符號(“=”)以及memcpy()等。那麼既然淺複製這麼簡單,為什麼還需要深複製呢?兩者之間的區別又是什麼呢?兩者分別在什麼情況下使用呢?
可能很多人寫了很久的程式碼,都還只知道物件賦值而不知道深複製,導致很多時間出現莫名bug而且找不到原因。今天就讓我們深入的來了解一下兩者的區別。
淺複製
例如:
class MyClass{public: MyClass(int x); ~MyClass(); int a ;private:};MyClass::MyClass(int x){ this->a = x ;}MyClass::~MyClass(){}void main(){ int b = 100 ;//淺複製 MyClass my1(10) ; MyClass my2 = my1;//淺複製 MyClass my3(20) ; memcpy(&my3,&my1,sizeof(MyClass));//淺複製 cout<<"my1.a:"<<my1.a<<endl; cout<<"my2.a:"<<my2.a<<endl; cout<<"my3.a:"<<my3.a<<endl; system("pause");}執行結果是都是10,以上三個都是淺複製,淺複製的原理就是:將 被複製物件所在記憶體中的資料按照二進位制位(Bit)複製到新物件所在的記憶體,這種預設的複製行為就是淺複製。其原理圖如下:
淺複製的缺點
上面的例子中,MyClass都是基本型別,這種淺複製用起來是沒有什麼問題,但是C++的難點是什麼?對,就是指標。如果MyClass成員中有指標型別怎麼辦?是不是也用淺複製呢?
我們在Mycalss類中新增一個char* str 成員,還是按上面那樣用淺複製的方式複製物件。
class MyClass{public: MyClass(int x,char * s); ~MyClass(); int len; int a ; char* str ;private:};MyClass::MyClass(int x,char* s ,int length){//這裡注意不能直接this->str = s ;因為指向的是常量,常量記憶體我們是delete不了的 this->len = lenth ; this->a = x ; this->str = new char[this->len] ; memcpy(str,s,this->len);}MyClass::~MyClass(){}void main(){ int b = 100 ;//淺複製 MyClass my1(10,"i love u") ; MyClass my2 = my1;//淺複製 MyClass my3(20,"i do not love u") ; memcpy(&my3,&my1,sizeof(MyClass)); cout<<"my1.a:"<<my1.a<<endl; cout<<"my2.a:"<<my2.a<<endl; cout<<"my3.a:"<<my3.a<<endl; cout<<"my1.str:"<<my1.str<<endl; cout<<"my2.str:"<<my2.str<<endl; cout<<"my3.str:"<<my3.str<<endl; system("pause");}執行起來好像沒有什麼問題;但是如果當我在my1透過my1的str指標將str指向的記憶體delete掉會發生什麼?cout<<"my1.str:"<<my1.str<<endl;delete[] my1.str;cout<<"delete OK"<<endl;cout<<"my2.str:"<<my2.str<<endl;cout<<"my3.str:"<<my3.str<<endl;我們發現my2.str和my3.str打印出來的是一堆亂碼。這是為什麼呢?請看下面的示意圖:
深複製的實現
既然淺複製在物件中存在著管理其他記憶體的指標時,在物件的記憶體釋放時會出現野指標的問題,那麼如何去避免呢?很簡單,除了會將原有物件的所有成員變數複製給新物件,還會為新物件再分配一塊記憶體,並將原有物件所持有的記憶體也複製過來。
實現程式碼:
#include <stdio.h>#include <string>#include <iostream>#include <complex>using namespace std;class MyClass{public: MyClass(int x,char * s,int len); MyClass(const MyClass& myclass); ~MyClass(); int len ; int a ; char* str;private:};MyClass::MyClass(int x, char* s,int length){ this->len = length ; this->a = x ; this->str = new char[this->len] ; memcpy(str,s,this->len);}MyClass::MyClass(const MyClass& myclass){ this->len = myclass.len ; this->a = myclass.a ; this->str = new char[this->len] ; memcpy(this->str,myclass.str,this->len);}MyClass::~MyClass(){}void main(){ cout<<sizeof(char)<<endl; int b = 100 ;//淺複製 MyClass my1(10,"i love u",6) ; MyClass my2 = my1;//淺複製 MyClass my3(20,"i do not love u",15) ; memcpy(&my3,&my1,sizeof(MyClass)); cout<<"my1.a:"<<my1.a<<endl; cout<<"my2.a:"<<my2.a<<endl; cout<<"my3.a:"<<my3.a<<endl; cout<<"my1.str:"<<my1.str<<endl; delete[] my1.str; cout<<"delete OK"<<endl; cout<<"my2.str:"<<my2.str<<endl; cout<<"my3.str:"<<my3.str<<endl; system("pause");}但是memcpy()方法是沒法使用深複製的,因為它是直接操作在記憶體上的;
c++中類的複製有兩種,深複製和淺複製。當出現類的等號賦值時,就會呼叫複製函式
兩者的區別,在未定義顯示複製建構函式的情況下,系統會呼叫預設的複製函式-淺複製它能夠完成成員的複製。當資料成員中沒有指標時淺複製是可行的,當資料成員中有指標時,就要使用深複製。
總之,深複製和淺複製的區別就是在物件狀態中包含其他物件的引用的時候,當複製一個物件,如果需要複製這個物件引用的物件時,則是深複製,否則是淺複製。