国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

C++類對象的復制-拷貝構造函數

2019-11-17 05:05:02
字體:
來源:轉載
供稿:網友

  在學習這一章內容前我們已經學習過了類的構造函數和析構函數的相關知識,對于普通類型的對象來說,他們之間的復制是很簡單的,例如: int a = 10;
int b =a;
  自己定義的類的對象同樣是對象,誰也不能阻止我們用以下的方式進行復制,例如:#include <iostream> 
using namespace std; 
 
class Test 

public: 
    Test(int temp) 
    { 
        p1=temp; 
    } 
PRotected: 
    int p1; 
 
}; 
 
void main() 

    Test a(99); 
    Test b=a; 
}  普通對象和類對象同為對象,他們之間的特性有相似之處也有不同之處,類對象內部存在成員變量,而普通對象是沒有的,當同樣的復制方法發生在不同的對象上的時候,那么系統對他們進行的操作也是不一樣的,就類對象而言,相同類型的類對象是通過拷貝構造函數來完成整個復制過程的,在上面的代碼中,我們并沒有看到拷貝構造函數,同樣完成了復制工作,這又是為什么呢?因為當一個類沒有自定義的拷貝構造函數的時候系統會自動提供一個默認的拷貝構造函數,來完成復制工作。

  下面,我們為了說明情況,就普通情況而言(以上面的代碼為例),我們來自己定義一個與系統默認拷貝構造函數一樣的拷貝構造函數,看看它的內部是如何工作的!

  代碼如下:#include <iostream> 
using namespace std; 
 
class Test 

public: 
    Test(int temp) 
    { 
        p1=temp; 
    } 
    Test(Test &c_t)//這里就是自定義的拷貝構造函數 
    { 
        cout<<"進入copy構造函數"<<endl; 
        p1=c_t.p1;//這句假如去掉就不能完成復制工作了,此句復制過程的核心語句 
    } 
public: 
    int p1; 
}; 
 
void main() 

    Test a(99); 
    Test b=a; 
    cout<<b.p1; 
    cin.get(); 
}  上面代碼中的Test(Test &c_t)就是我們自定義的拷貝構造函數,拷貝構造函數的名稱必須與類名稱一致,函數的形式參數是本類型的一個引用變量,且必須是引用。

  當用一個已經初始化過了的自定義類類型對象去初始化另一個新構造的對象的時候,拷貝構造函數就會被自動調用,假如你沒有自定義拷貝構造函數的時候系統將會提供給一個默認的拷貝構造函數來完成這個過程,上面代碼的復制核心語句就是通過Test(Test &c_t)拷貝構造函數內的p1=c_t.p1;語句完成的。假如取掉這句代碼,那么b對象的p1屬性將得到一個未知的隨機值; 更多文章 更多內容請看C/C++技術專題專題,或   下面我們來討論一下關于淺拷貝和深拷貝的問題。


  就上面的代碼情況而言,很多人會問到,既然系統會自動提供一個默認的拷貝構造函數來處理復制,那么我們沒有意義要去自定義拷貝構造函數呀,對,就普通情況而言這的確是沒有必要的,但在某寫狀況下,類體內的成員是需要開辟動態開辟堆內存的,假如我們不自定義拷貝構造函數而讓系統自己處理,那么就會導致堆內存的所屬權產生混亂,試想一下,已經開辟的一端堆地址原來是屬于對象a的,由于復制過程發生,b對象取得是a已經開辟的堆地址,一旦程序產生析構,釋放堆的時候,計算機是不可能清楚這段地址是真正屬于誰的,當連續發生兩次析構的時候就出現了運行錯誤。

  為了更具體的說明問題,請看如下的代碼。 #include <iostream> 
using namespace std; 
 
class Internet 

public: 
    Internet(char *name,char *address) 
    { 
        cout<<"載入構造函數"<<endl; 
        strcpy(Internet::name,name); 
        strcpy(Internet::address,address); 
        cname=new char[strlen(name)+1]; 
        if(cname!=NULL) 
        { 
            strcpy(Internet::cname,name); 
        } 
    } 
    Internet(Internet &temp) 
    { 
        cout<<"載入COPY構造函數"<<endl; 
        strcpy(Internet::name,temp.name); 
        strcpy(Internet::address,temp.address); 
        cname=new char[strlen(name)+1];//這里注重,深拷貝的體現! 
        if(cname!=NULL) 
        { 
            strcpy(Internet::cname,name); 
        } 
    } 
    ~Internet() 
    { 
        cout<<"載入析構函數!"; 
        delete[] cname; 
        cin.get(); 
    } 
    void show(); 
protected: 
    char name[20]; 
    char address[30]; 

    char *cname; 
}; 
void Internet::show() 

    cout<<name<<":"<<address<<cname<<endl; 

void test(Internet ts) 

    cout<<"載入test函數"<<endl; 

void main() 

    Internet a("中國軟件開發實驗室","www.cndev-lab.com"); 
    Internet b = a; 
    b.show(); 
    test(b); 
}  上面代碼就演示了深拷貝的問題,對對象b的cname屬性采取了新開辟內存的方式避免了內存歸屬不清所導致析構釋放空間時候的錯誤,最后我必須提一下,對于上面的程序我的解釋并不多,就是希望讀者本身運行程序觀察變化,進而深刻理解。   拷貝和淺拷貝的定義可以簡單理解成:假如一個類擁有資源(堆,或者是其它系統資源),當這個類的對象發生復制過程的時候,這個過程就可以叫做深拷貝,反之對象存在資源但復制過程并未復制資源的情況視為淺拷貝。


  淺拷貝資源后在釋放資源的時候會產生資源歸屬不清的情況導致程序運行出錯,這點尤其需要注重!  以前我們的教程中討論過函數返回對象產生臨時變量的問題,接下來我們來看一下在函數中返回自定義類型對象是否也遵循此規則產生臨時對象!

更多文章 更多內容請看C/C++技術專題專題,或   先運行下列代碼:#include <iostream> 
using namespace std; 
 
class Internet 

public: 
    Internet() 
    { 
         
    }; 
    Internet(char *name,char *address) 
    { 
        cout<<"載入構造函數"<<endl; 
        strcpy(Internet::name,name); 
    } 
    Internet(Internet &temp) 
    { 
        cout<<"載入COPY構造函數"<<endl; 
        strcpy(Internet::name,temp.name); 
        cin.get(); 
    } 
    ~Internet() 
    { 
        cout<<"載入析構函數!"; 
        cin.get(); 
    } 
protected: 

    char name[20]; 
    char address[20]; 
}; 
Internet tp() 

    Internet b("中國軟件開發實驗室","www.cndev-lab.com"); 
    return b; 

void main() 

    Internet a; 
    a=tp(); 
}  從上面的代碼運行結果可以看出,程序一共載入過析構函數三次,證實了由函數返回自定義類型對象同樣會產生臨時變量,事實上對象a得到的就是這個臨時Internet類類型對象temp的值。  這一下節的內容我們來說一下無名對象。   利用無名對象初始化對象系統不會不調用拷貝構造函數。

  那么什么又是無名對象呢?

  很簡單,假如在上面程序的main函數中有:

  Internet ("中國軟件開發實驗室","www.cndev-lab.com");

  這樣的一句語句就會產生一個無名對象,無名對象會調用構造函數但利用無名對象初始化對象系統不會不調用拷貝構造函數!

  下面三段代碼是很見到的三種利用無名對象初始化對象的例子。#include <iostream> 
using namespace std; 
 
class Internet 

public: 
    Internet(char *name,char *address) 
    { 
        cout<<"載入構造函數"<<endl; 
        strcpy(Internet::name,name); 
    } 
    Internet(Internet &temp) 
    { 
        cout<<"載入COPY構造函數"<<endl; 
        strcpy(Internet::name,temp.name); 
        cin.get(); 
    } 
    ~Internet() 
    { 
        cout<<"載入析構函數!"; 
    } 
public: 
    char name[20]; 
    char address[20]; 
}; 
 
void main() 

    Internet a=Internet("中國軟件開發實驗室","www.cndev-lab.com"); 
    cout<<a.name; 
    cin.get(); 
}  上面代碼的運行結果有點“出人意料”,從思維邏輯上說,當無名對象創建了后,是應該調用自定義拷貝構造函數,或者是默認拷貝構造函數來完成復制過程的,但事實上系統并沒有這么做,因為無名對象使用過后在整個程序中就失去了作用,對于這種情況c++會把代碼看成是: Internet a("中國軟件開發實驗室",www.cndev-lab.com);   省略了創建無名對象這一過程,所以說不會調用拷貝構造函數。 更多文章 更多內容請看C/C++技術專題專題,或   最后讓我們來看看引用無名對象的情況。#include <iostream>   
using namespace std;   
   
class Internet   

{   
public:   
    Internet(char *name,char *address)   
    {   
        cout<<"載入構造函數"<<endl;   
        strcpy(Internet::name,name);   
    }   
    Internet(Internet &temp)   
    {   
        cout<<"載入COPY構造函數"<<endl;   
        strcpy(Internet::name,temp.name);   
        cin.get();   
    }   
    ~Internet()   
    {   
        cout<<"載入析構函數!";   
    }   
public:   
    char name[20];   
    char address[20];   
};   
   
void main()   
{   
    Internet &a=Internet("中國軟件開發實驗室","www.cndev-lab.com");   
    cout<<a.name; 
    cin.get();   
}  引用本身是對象的別名,和復制并沒有關系,所以不會調用拷貝構造函數,但要注重的是,在c++看來:

Internet &a=Internet("中國軟件開發實驗室","www.cndev-lab.com");

  是等價與:

Internet a("中國軟件開發實驗室","www.cndev-lab.com");

  的,注重觀察調用析構函數的位置(這種情況是在main()外調用,而無名對象本身是在main()內析構的)。 更多文章 更多內容請看C/C++技術專題專題,或

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 上饶市| 延寿县| 开远市| 武功县| 比如县| 汕尾市| 济宁市| 新蔡县| 中山市| 绥德县| 遂宁市| 清丰县| 青阳县| 朝阳区| 玉溪市| 肇州县| 蒲城县| 连云港市| 油尖旺区| 清远市| 灵山县| 永平县| 阿合奇县| 尤溪县| 绍兴县| 万全县| 铁岭县| 西乌珠穆沁旗| 长汀县| 永和县| 宝坻区| 永春县| 兴仁县| 德清县| 察雅县| 故城县| 东乌| 容城县| 博客| 泸定县| 万源市|