最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

c++詳細(xì)講解構(gòu)造函數(shù)的拷貝流程

 更新時間:2022年05月09日 09:07:51   作者:panamera12  
拷貝構(gòu)造函數(shù)是一種特殊的構(gòu)造函數(shù),它在創(chuàng)建對象時,是使用同一類中之前創(chuàng)建的對象來初始化新創(chuàng)建的對象。拷貝構(gòu)造函數(shù)通常用于:通過使用另一個同類型的對象來初始化新創(chuàng)建的對象。?復(fù)制對象把它作為參數(shù)傳遞給函數(shù)。復(fù)制對象,并從函數(shù)返回這個對象
#include <iostream>
#include <string>
using namespace std;
void func(string str){
    cout<<str<<endl;
}
int main(){
    string s1 = "http:www.biancheng.net";
    string s2(s1);
    string s3 = s1;
    string s4 = s1 + " " + s2;
    func(s1);
    cout<<s1<<endl<<s2<<endl<<s3<<endl<<s4<<endl;
    return 0;
}

運行結(jié)果:

http:www.biancheng.net

http:www.biancheng.net

http:www.biancheng.net

http:www.biancheng.net

http:www.biancheng.net http:www.biancheng.net

s1、s2、s3、s4 以及 func() 的形參 str,都是使用拷貝的方式來初始化的。

對于 s1、s2、s3、s4,都是將其它對象的數(shù)據(jù)拷貝給當(dāng)前對象,以完成當(dāng)前對象的初始化。

對于 func() 的形參 str,其實在定義時就為它分配了內(nèi)存,但是此時并沒有初始化,只有等到調(diào)用 func() 時,才會將其它對象的數(shù)據(jù)拷貝給 str 以完成初始化。

當(dāng)以拷貝的方式初始化一個對象時,會調(diào)用一個特殊的構(gòu)造函數(shù),就是拷貝構(gòu)造函數(shù)(Copy Constructor)。

#include <iostream>
#include <string>
using namespace std;
class Student{
public:
    Student(string name = "", int age = 0, float score = 0.0f);  //普通構(gòu)造函數(shù)
    Student(const Student &stu);  //拷貝構(gòu)造函數(shù)(聲明)
public:
    void display();
private:
    string m_name;
    int m_age;
    float m_score;
};
Student::Student(string name, int age, float score): m_name(name), m_age(age), m_score(score){ }
//拷貝構(gòu)造函數(shù)(定義)
Student::Student(const Student &stu){
    this->m_name = stu.m_name;
    this->m_age = stu.m_age;
    this->m_score = stu.m_score;
    cout<<"Copy constructor was called."<<endl;
}
void Student::display(){
    cout<<m_name<<"的年齡是"<<m_age<<",成績是"<<m_score<<endl;
}
int main(){
   const Student stu1("小明", 16, 90.5);
    Student stu2 = stu1;  //調(diào)用拷貝構(gòu)造函數(shù)
    Student stu3(stu1);  //調(diào)用拷貝構(gòu)造函數(shù)
    stu1.display();
    stu2.display();
    stu3.display();
    return 0;
}

運行結(jié)果:

Copy constructor was called.

Copy constructor was called.

小明的年齡是16,成績是90.5

小明的年齡是16,成績是90.5

小明的年齡是16,成績是90.5

第 8 行是拷貝構(gòu)造函數(shù)的聲明,第 20 行是拷貝構(gòu)造函數(shù)的定義??截悩?gòu)造函數(shù)只有一個參數(shù),它的類型是當(dāng)前類的引用,而且一般都是 const 引用。

1) 為什么必須是當(dāng)前類的引用呢?

如果拷貝構(gòu)造函數(shù)的參數(shù)不是當(dāng)前類的引用,而是當(dāng)前類的對象,那么在調(diào)用拷貝構(gòu)造函數(shù)時,會將另外一個對象直接傳遞給形參,這本身就是一次拷貝,會再次調(diào)用拷貝構(gòu)造函數(shù),然后又將一個對象直接傳遞給了形參,將繼續(xù)調(diào)用拷貝構(gòu)造函數(shù)……這個過程會一直持續(xù)下去,沒有盡頭,陷入死循環(huán)。

只有當(dāng)參數(shù)是當(dāng)前類的引用時,才不會導(dǎo)致再次調(diào)用拷貝構(gòu)造函數(shù),這不僅是邏輯上的要求,也是 C++ 語法的要求。

2) 為什么是 const 引用呢?

拷貝構(gòu)造函數(shù)的目的是用其它對象的數(shù)據(jù)來初始化當(dāng)前對象,并沒有期望更改其它對象的數(shù)據(jù),添加 const 限制后,這個含義更加明確了。

另外一個原因是,添加 const 限制后,可以將 const 對象和非 const 對象傳遞給形參了,因為非 const 類型可以轉(zhuǎn)換為 const 類型。如果沒有 const 限制,就不能將 const 對象傳遞給形參,因為 const 類型不能轉(zhuǎn)換為非 const 類型,這就意味著,不能使用 const 對象來初始化當(dāng)前對象了。

當(dāng)然,你也可以再添加一個參數(shù)為非const 引用的拷貝構(gòu)造函數(shù),這樣就不會出錯了。換句話說,一個類可以同時存在兩個拷貝構(gòu)造函數(shù),一個函數(shù)的參數(shù)為 const 引用,另一個函數(shù)的參數(shù)為非 const 引用。

class Base{
public:
    Base(): m_a(0), m_b(0){ }
    Base(int a, int b): m_a(a), m_b(b){ }
private:
    int m_a;
    int m_b;
};
int main(){
    int a = 10;
    int b = a;  //拷貝
    Base obj1(10, 20);
    Base obj2 = obj1;  //拷貝
    return 0;
}

b 和 obj2 都是以拷貝的方式初始化的,具體來說,就是將 a 和 obj1 所在內(nèi)存中的數(shù)據(jù)按照二進(jìn)制位(Bit)復(fù)制到 b 和 obj2 所在的內(nèi)存,這種默認(rèn)的拷貝行為就是淺拷貝,這和調(diào)用 memcpy() 函數(shù)的效果非常類似。

對于簡單的類,默認(rèn)的拷貝構(gòu)造函數(shù)一般就夠用了,我們也沒有必要再顯式地定義一個功能類似的拷貝構(gòu)造函數(shù)。但是當(dāng)類持有其它資源時,例如動態(tài)分配的內(nèi)存、指向其他數(shù)據(jù)的指針等,默認(rèn)的拷貝構(gòu)造函數(shù)就不能拷貝這些資源了,我們必須顯式地定義拷貝構(gòu)造函數(shù),以完整地拷貝對象的所有數(shù)據(jù)。

下面我們通過一個具體的例子來說明顯式定義拷貝構(gòu)造函數(shù)的必要性。

#include <iostream>
#include <cstdlib>
using namespace std;
//變長數(shù)組類
class Array{
public:
    Array(int len);
    Array(const Array &arr);  //拷貝構(gòu)造函數(shù)
    ~Array();
public:
    int operator[](int i) const { return m_p[i]; }  //獲取元素(讀?。?
    int &operator[](int i){ return m_p[i]; }  //獲取元素(寫入)
    int length() const { return m_len; }
private:
    int m_len;
    int *m_p;
};
Array::Array(int len): m_len(len){
    m_p = (int*)calloc( len, sizeof(int) );
}
Array::Array(const Array &arr){  //拷貝構(gòu)造函數(shù)
    this->m_len = arr.m_len;
    this->m_p = (int*)calloc( this->m_len, sizeof(int) );
    memcpy( this->m_p, arr.m_p, m_len * sizeof(int) );
}
Array::~Array(){ free(m_p); }
//打印數(shù)組元素
void printArray(const Array &arr){
    int len = arr.length();
    for(int i=0; i<len; i++){
        if(i == len-1){
            cout<<arr[i]<<endl;
        }else{
            cout<<arr[i]<<", ";
        }
    }
}
int main(){
    Array arr1(10);
    for(int i=0; i<10; i++){
        arr1[i] = i;
    }
    Array arr2 = arr1;
    arr2[5] = 100;
    arr2[3] = 29;
    printArray(arr1);
    printArray(arr2);
    return 0;
}

運行結(jié)果:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9

0, 1, 2, 29, 4, 100, 6, 7, 8, 9

本例中我們顯式地定義了拷貝構(gòu)造函數(shù),它除了會將原有對象的所有成員變量拷貝給新對象,還會為新對象再分配一塊內(nèi)存,并將原有對象所持有的內(nèi)存也拷貝過來。這樣做的結(jié)果是,原有對象和新對象所持有的動態(tài)內(nèi)存是相互獨立的,更改一個對象的數(shù)據(jù)不會影響另外一個對象,本例中我們更改了 arr2 的數(shù)據(jù),就沒有影響 arr1。

這種將對象所持有的其它資源一并拷貝的行為叫做深拷貝,我們必須顯式地定義拷貝構(gòu)造函數(shù)才能達(dá)到深拷貝的目的。深拷貝的例子比比皆是,除了上面的變長數(shù)組類,使用的動態(tài)數(shù)組類也需要深拷貝;此外,標(biāo)準(zhǔn)模板庫(STL)中的 string、vector、stack、set、map 等也都必須使用深拷貝。

讀者如果希望親眼目睹不使用深拷貝的后果,可以將上例中的拷貝構(gòu)造函數(shù)刪除,那么運行結(jié)果將變?yōu)椋?, 1, 2, 29, 4, 100, 6, 7, 8, 9

0, 1, 2, 29, 4, 100, 6, 7, 8, 9

可以發(fā)現(xiàn),更改 arr2 的數(shù)據(jù)也影響到了 arr1。這是因為,在創(chuàng)建 arr2 對象時,默認(rèn)拷貝構(gòu)造函數(shù)將 arr1.m_p 直接賦值給了 arr2.m_p,導(dǎo)致 arr2.m_p 和 arr1.m_p 指向了同一塊內(nèi)存,所以會相互影響。

另外需要注意的是,printArray() 函數(shù)的形參為引用類型,這樣做能夠避免在傳參時調(diào)用拷貝構(gòu)造函數(shù);又因為 printArray() 函數(shù)不會修改任何數(shù)組元素,所以我們添加了 const 限制,以使得語義更加明確。

  • 到底是淺拷貝還是深拷貝

如果一個類擁有指針類型的成員變量,那么絕大部分情況下就需要深拷貝,因為只有這樣,才能將指針指向的內(nèi)容再復(fù)制出一份來,讓原有對象和新生對象相互獨立,彼此之間不受影響。如果類的成員變量沒有指針,一般淺拷貝足以。

另外一種需要深拷貝的情況就是在創(chuàng)建對象時進(jìn)行一些預(yù)處理工作,比如統(tǒng)計創(chuàng)建過的對象的數(shù)目、記錄對象創(chuàng)建的時間等,請看下面的例子:

#include <iostream>
#include <ctime>
#include <windows.h>  //在Linux和Mac下要換成 unistd.h 頭文件
using namespace std;
class Base{
public:
    Base(int a = 0, int b = 0);
    Base(const Base &obj);  //拷貝構(gòu)造函數(shù)
public:
    int getCount() const { return m_count; }
    time_t getTime() const { return m_time; }
private:
    int m_a;
    int m_b;
    time_t m_time;  //對象創(chuàng)建時間
    static int m_count;  //創(chuàng)建過的對象的數(shù)目
};
int Base::m_count = 0;
Base::Base(int a, int b): m_a(a), m_b(b){
    m_count++;
    m_time = time((time_t*)NULL);
}
Base::Base(const Base &obj){  //拷貝構(gòu)造函數(shù)
    this->m_a = obj.m_a;
    this->m_b = obj.m_b;
    this->m_count++;
    this->m_time = time((time_t*)NULL);
}
int main(){
    Base obj1(10, 20);
    cout<<"obj1: count = "<<obj1.getCount()<<", time = "<<obj1.getTime()<<endl;
    Sleep(3000);  //在Linux和Mac下要寫作 sleep(3);
    Base obj2 = obj1;
    cout<<"obj2: count = "<<obj2.getCount()<<", time = "<<obj2.getTime()<<endl;
    return 0;
}

運行結(jié)果:

obj1: count = 1, time = 1488344372

obj2: count = 2, time = 1488344375

運行程序,先輸出第一行結(jié)果,等待 3 秒后再輸出第二行結(jié)果。Base 類中的 m_time 和 m_count 分別記錄了對象的創(chuàng)建時間和創(chuàng)建數(shù)目,它們在不同的對象中有不同的值,所以需要在初始化對象的時候提前處理一下,這樣淺拷貝就不能勝任了,就必須使用深拷貝了。

到此這篇關(guān)于c++詳細(xì)講解構(gòu)造函數(shù)的拷貝流程的文章就介紹到這了,更多相關(guān)c++構(gòu)造函數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 基于Matlab實現(xiàn)離散系統(tǒng)分岔圖的繪制

    基于Matlab實現(xiàn)離散系統(tǒng)分岔圖的繪制

    這篇文章主要介紹了如何利用Matlab實現(xiàn)離散分岔圖的繪制,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)Matlab有一定的幫助,需要的可以參考一下
    2022-04-04
  • MFC實現(xiàn)在文件尾追加數(shù)據(jù)的方法

    MFC實現(xiàn)在文件尾追加數(shù)據(jù)的方法

    這篇文章主要介紹了MFC實現(xiàn)在文件尾追加數(shù)據(jù)的方法,涉及MFC文件操作的相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-09-09
  • 詳解C++中的析構(gòu)函數(shù)

    詳解C++中的析構(gòu)函數(shù)

    這篇文章主要介紹了C++中的析構(gòu)函數(shù)的相關(guān)知識,文中講解非常詳細(xì),代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-06-06
  • C及C++中typedef的簡單使用介紹

    C及C++中typedef的簡單使用介紹

    C/C++中關(guān)鍵字typedef的理解不是多透徹,今天小編抽空給大家分享下C及C++中typedef的簡單使用介紹,需要的朋友可以參考下
    2016-10-10
  • C++中的boost::function庫簡介

    C++中的boost::function庫簡介

    這篇文章介紹了C++中的boost::function庫,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • c語言中字符串分割函數(shù)及實現(xiàn)方法

    c語言中字符串分割函數(shù)及實現(xiàn)方法

    下面小編就為大家?guī)硪黄猚語言中字符串分割函數(shù)及實現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-05-05
  • C++ OpenCV實戰(zhàn)之網(wǎng)孔檢測的實現(xiàn)

    C++ OpenCV實戰(zhàn)之網(wǎng)孔檢測的實現(xiàn)

    這篇文章主要介紹了如何利用C++和OpenCV實現(xiàn)網(wǎng)孔檢測,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)OpenCV有一定幫助,感興趣的小伙伴可以了解一下
    2022-05-05
  • C語言計算代碼執(zhí)行所耗CPU時鐘周期

    C語言計算代碼執(zhí)行所耗CPU時鐘周期

    本文給大家介紹的是使用C語言來計算代碼執(zhí)行所耗CPU時鐘周期的代碼,非常的簡單實用,不過要依托于sync,有需要的小伙伴自己參考下吧。
    2015-03-03
  • FFmpeg實戰(zhàn)之利用ffplay實現(xiàn)自定義輸入流播放

    FFmpeg實戰(zhàn)之利用ffplay實現(xiàn)自定義輸入流播放

    ffplay是FFmpeg提供的一個極為簡單的音視頻媒體播放器,可以用于音視頻播放、可視化分析。本文將利用ffplay實現(xiàn)自定義輸入流播放,需要的可以參考一下
    2022-12-12
  • C++代碼規(guī)范之命名規(guī)則

    C++代碼規(guī)范之命名規(guī)則

    以下是對C++中的命名規(guī)則進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以參考下
    2013-07-07

最新評論

嘉义县| 城固县| 左权县| 聂拉木县| 林周县| 湄潭县| 墨脱县| 罗江县| 登封市| 丹凤县| 凌海市| 抚宁县| 新乡市| 阳春市| 金平| 仪征市| 周口市| 响水县| 宜良县| 那曲县| 时尚| 赤峰市| 塔河县| 东台市| 乌鲁木齐市| 灌云县| 灵丘县| 通渭县| 铜陵市| 永新县| 清河县| 米脂县| 平湖市| 运城市| 二连浩特市| 漯河市| 金溪县| 望奎县| 阳春市| 敖汉旗| 迁西县|