C++深復(fù)制和淺復(fù)制

對于普通類型的對象來說,它們之間的復(fù)制是很簡單的,例如:
int a=88;
int b=a;
double f=3.12;
double d(f);

成都創(chuàng)新互聯(lián)是一家集網(wǎng)站建設(shè),綠春企業(yè)網(wǎng)站建設(shè),綠春品牌網(wǎng)站建設(shè),網(wǎng)站定制,綠春網(wǎng)站建設(shè)報價,網(wǎng)絡(luò)營銷,網(wǎng)絡(luò)優(yōu)化,綠春網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競爭力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時我們時刻保持專業(yè)、時尚、前沿,時刻以成就客戶成長自我,堅持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實用型網(wǎng)站。

而類對象與普通對象不同,類對象內(nèi)部結(jié)構(gòu)一般較為復(fù)雜,存在各種數(shù)據(jù)成員。

#include <iostream>  
using namespace std;  
class Test  
{  
private:  
    int a,b;  
public:  
    Test(int x, int y)     //提供的形式參數(shù),是為了給數(shù)據(jù)成員直接初始化的  
    {  
        a=x;  
        b=y;  
    }  
    Test(const Test& C)   //復(fù)制構(gòu)造函數(shù),提供一個同類型對象作為參數(shù)  
    {  
        a=C.a;  
        b=C.b;  
    }  
    void show ()  
    {  
        cout<<a<<"  "<<b<<endl;  
    }  
};  

int main()  
{  
    Test a(100,10);    //執(zhí)行構(gòu)造函數(shù)Test::Test(int x, int y)  
    Test b(a);      //執(zhí)行構(gòu)造函數(shù)Test::Test(const Test& C)  
    Test c=a;      //也執(zhí)行構(gòu)造函數(shù)Test::Test(const Test& C)  
    b.show();  
    c.show();  
    return 0;  
}  
 
 運(yùn)行程序,屏幕輸出兩行100   10。
    從以上代碼的運(yùn)行結(jié)果可以看出,系統(tǒng)在聲明對象b和c時,完成了由對象a的復(fù)制。
    復(fù)制構(gòu)造函數(shù),就類對象而言,相同類型的類對象是通過復(fù)制構(gòu)造函數(shù)來完成整個復(fù)制過程的。
    上例中的Test::Test(const Test& C),就是我們自定義的復(fù)制構(gòu)造函數(shù)。可見,復(fù)制構(gòu)造函數(shù)是一種特殊的構(gòu)造函數(shù),函數(shù)的名稱必須和類名稱一致,它的唯一的一個參數(shù)是本類型的一個引用變量,該參數(shù)是const類型,用來約束作為參數(shù)的對象在構(gòu)造新對象中是不能被改變的。
    當(dāng)用一個已初始化過了的自定義類類型對象去初始化另一個新構(gòu)造的對象的時候,復(fù)制構(gòu)造函數(shù)就會被自動調(diào)用。也就是說,當(dāng)類的對象需要復(fù)制時,復(fù)制構(gòu)造函數(shù)將會被調(diào)用。以下情況都會調(diào)用復(fù)制構(gòu)造函數(shù):

一個對象以值傳遞的方式傳入函數(shù)體
一個對象以值傳遞的方式從函數(shù)返回
一個對象需要通過另外一個對象進(jìn)行初始化。
如果在類中沒有顯式地聲明一個復(fù)制構(gòu)造函數(shù),那么,編譯器將會自動生成一個默認(rèn)的復(fù)制構(gòu)造函數(shù),該構(gòu)造函數(shù)完成對象之間的淺復(fù)制,后面將進(jìn)行說明。
自定義復(fù)制構(gòu)造函數(shù)是一種良好的編程風(fēng)格,它可以阻止編譯器形成默認(rèn)的復(fù)制構(gòu)造函數(shù),提高源碼效率。

淺復(fù)制和深復(fù)制
所謂淺復(fù)制,如同上面出現(xiàn)過的構(gòu)造函數(shù)中處理的一樣,直接為數(shù)據(jù)成員賦值即可。在很多情況下,這是可以的。創(chuàng)建新的對象,要為對象的數(shù)據(jù)成員分配存儲空間,直接賦值就將值保存在相應(yīng)的空間中。然而,這種淺復(fù)制,卻并不能通行天下,下面的程序中,淺復(fù)制帶來了問題。

#include <iostream>  
#include <cstring>  
using namespace std;  
class Test  
{  
private:  
    int a;  
    char *str;  
public:  
    Test(int b, char *s)  
    {  
        a=b;  
        strcpy(str,s);  //肇事地點,但不是禍端  
    }  
    Test(const Test& C)  
    {  
        a=C.a;  
        strcpy(str,C.str);  
    }  
    void show ()  
    {  
        cout<<a<<","<<str<<endl;  
    }  
};  

int main()  
{  
    Test a(100,"hello");  
    Test b(a);  
    a.show();  
    b.show();  
    return 0;  
}  
    程序運(yùn)行中,會彈出一個窗口:程序的執(zhí)行意外停止了。面對這個窗口,我們應(yīng)該有感覺,這和使用指針不當(dāng)有關(guān)系。

我們從main函數(shù)看起。

當(dāng)程序執(zhí)行到第28行Test a(100,"hello");時,對象a的數(shù)據(jù)成員a獲得實際參數(shù)100的值,而數(shù)據(jù)成員str,即指針,是個隨機(jī)地址值(指針的值,非指針指向的值)!在程序中,試圖通過strcpy(str,s);將形式參數(shù)s指向的字符串"hello",復(fù)制到str所指向的那個位置,而那個位置,其地址并不是經(jīng)過系統(tǒng)分配來的,這是個危險的操作。在這里,str這樣未經(jīng)過分配的地址(指針),被稱為“野指針”。

在執(zhí)行第29行Test b(a);時,同樣的事情還要發(fā)生。

str這樣的野指針是多么的霸道!在有的系統(tǒng)里,這樣的行為是被接受的,可以想到其有多危險。有些系統(tǒng)中,不允許這樣的事情發(fā)生的。于是,上面的程序在codeblock中調(diào)試會出錯。這個錯來的好。

解決這樣的問題的方法,就是在構(gòu)造函數(shù)中,要為指針類型的成員,分配專門的空間。以這條規(guī)則構(gòu)建的復(fù)制,稱作為深復(fù)制!

上面的程序,改寫為:

#include <iostream>  
#include <cstring>  
using namespace std;  
class Test  
{  
private:  
    int a;  
    char *str;  
public:  
    Test(int b, char *s)  
    {  
        a=b;  
        str=new char[strlen(s)+1];  //分配str指向的空間,其長度根據(jù)s指向的字符串定。為何加1?字符串結(jié)束要用\0  
        strcpy(str,s);  //前一程序的肇事地點,禍端已經(jīng)由上一句摘除  
    }  
    Test(const Test& C)  
    {  
        a=C.a;  
        str=new char[strlen(C.str)+1];   //同上,這樣str就不是野指針了  
        strcpy(str,C.str);  
    }  
    ~Test()  
    {  
        delete []str;  
    }  
    void show ()  
    {  
        cout<<a<<","<<str<<endl;  
    }  
};  

int main()  
{  
    Test a(100,"hello");  
    Test b(a);  
    a.show();  
    b.show();  
    return 0;  
}  
```  
        好了,a和b對象的str成員,明確地給分配了空間,他們再不是野指針了。因為明確地分配了空間,析構(gòu)函數(shù)中要釋放對應(yīng)的空間。我們不能用野指針,當(dāng)然,也不能對象要撤銷了,還占著空間不放,做事不能這樣不厚道。
        深復(fù)制就體現(xiàn)在第13和第19行分配指針指向的空間,這段空間的地址,也將是指針的值(分清指針的值和指針指向的值)。

        下面再給一個例子,類A的數(shù)據(jù)成員可以保存len個整型數(shù)據(jù)。類中的數(shù)據(jù)成員arrayAddr是指向整型的指針,可以作為一個一元數(shù)組的起始地址。這個類有指針數(shù)據(jù)成員,構(gòu)造函數(shù)的定義中,必須要采用深復(fù)制的方法,第16行體現(xiàn)了這一點。另外,析構(gòu)函數(shù)中完成了對分配的空間的釋放

#include<iostream>
using namespace std;
class A
{
private:
int arrayAddr;//保存一個有l(wèi)en個整型元素的數(shù)組的首地址
int len; //記錄動態(tài)數(shù)組的長度
public:
A(int
a, int n);
~A();
int sum();
};
A::A(int *a, int n)
{
len=n;
arrayAddr=new int[n]; //為指針數(shù)據(jù)成員分配空間,注意,沒有上面例子中加1那回事
for(int i=0; i<n; i++) //逐個地將a指向的值逐個地復(fù)制過來
{
arrayAddr[i]=a[i];
}
}
//析構(gòu)函數(shù)的類外定義,釋放指針型數(shù)據(jù)a所指向的空間
A::~A()
{
delete [] arrayAddr;
}
int A::sum() //獲得a指向的數(shù)組中下標(biāo)為i的元素的值
{
int s=0;
for(int i=0; i<len; i++) //逐個地將a指向的值逐個地復(fù)制過來
{
s+=arrayAddr[i];
}
return s;
}

int main(){
int b[10]= {75, 99, 90, 93, 38, 15, 5, 7, 52, 4};
A r1(b,10);
cout<<"和:"<<r1.sum()<<endl;
int c[15] = {18,68,10,52,3,19,12,100,56,96,95,97,1,4,93};
A r2(c,15);
cout<<"和:"<<r2.sum()<<endl;
return 0;
}

網(wǎng)站標(biāo)題:C++深復(fù)制和淺復(fù)制
URL標(biāo)題:http://bm7419.com/article8/jdjoip.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供靜態(tài)網(wǎng)站、企業(yè)建站、域名注冊、響應(yīng)式網(wǎng)站、品牌網(wǎng)站建設(shè)、建站公司

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站建設(shè)網(wǎng)站維護(hù)公司