C++中的拷貝構(gòu)造函數(shù)詳解
C++拷貝構(gòu)造函數(shù)(復(fù)制構(gòu)造函數(shù))詳解
拷貝和復(fù)制是一個(gè)意思,對(duì)應(yīng)的英文單詞都是copy。對(duì)于計(jì)算機(jī)來(lái)說(shuō),拷貝是指用一份原有的、已經(jīng)存在的數(shù)據(jù)創(chuàng)建出一份新的數(shù)據(jù),最終的結(jié)果是多了一份相同的數(shù)據(jù)。例如,將 Word 文檔拷貝到U盤(pán)去復(fù)印店打印,將 D 盤(pán)的圖片拷貝到桌面以方便瀏覽,將重要的文件上傳到百度網(wǎng)盤(pán)以防止丟失等,都是「創(chuàng)建一份新數(shù)據(jù)」的意思。
在 C++ 中,拷貝并沒(méi)有脫離它本來(lái)的含義,只是將這個(gè)含義進(jìn)行了“特化”,是指用已經(jīng)存在的對(duì)象創(chuàng)建出一個(gè)新的對(duì)象。從本質(zhì)上講,對(duì)象也是一份數(shù)據(jù),因?yàn)樗鼤?huì)占用內(nèi)存。
嚴(yán)格來(lái)說(shuō),對(duì)象的創(chuàng)建包括兩個(gè)階段,首先要分配內(nèi)存空間,然后再進(jìn)行初始化:
分配內(nèi)存很好理解,就是在堆區(qū)、棧區(qū)或者全局?jǐn)?shù)據(jù)區(qū)留出足夠多的字節(jié)。這個(gè)時(shí)候的內(nèi)存還比較“原始”,沒(méi)有被“教化”,它所包含的數(shù)據(jù)一般是零值或者隨機(jī)值,沒(méi)有實(shí)際的意義。
初始化就是首次對(duì)內(nèi)存賦值,讓它的數(shù)據(jù)有意義。注意是首次賦值,再次賦值不叫初始化。初始化的時(shí)候還可以為對(duì)象分配其他的資源(打開(kāi)文件、連接網(wǎng)絡(luò)、動(dòng)態(tài)分配內(nèi)存等),或者提前進(jìn)行一些計(jì)算(根據(jù)價(jià)格和數(shù)量計(jì)算出總價(jià)、根據(jù)長(zhǎng)度和寬度計(jì)算出矩形的面積等)等。說(shuō)白了,初始化就是調(diào)用構(gòu)造函數(shù)。
很明顯,這里所說(shuō)的拷貝是在初始化階段進(jìn)行的,也就是用其它對(duì)象的數(shù)據(jù)來(lái)初始化新對(duì)象的內(nèi)存。
那么,如何用拷貝的方式來(lái)初始化一個(gè)對(duì)象呢?其實(shí)這樣的例子比比皆是,string 類就是一個(gè)典型的例子。
#include <iostream> #include <string> using namespace std; void func(string str){ cout<<str<<endl; } int main(){ string s1 = "http://c.ttt.net"; string s2(s1); string s3 = s1; string s4 = s1 + " " + s2; func(s1); cout<<s1<<endl<<s2<<endl<<s3<<endl<<s4<<endl; return 0; } 運(yùn)行結(jié)果: http://c.ttt.net http://c.ttt.net http://c.ttt.net http://c.ttt.net http://c.ttt.net http://c.ttt.net
s1、s2、s3、s4 以及 func() 的形參 str,都是使用拷貝的方式來(lái)初始化的。
對(duì)于 s1,表面上看起來(lái)是將一個(gè)字符串直接賦值給了 s1,實(shí)際上在內(nèi)部進(jìn)行了類型轉(zhuǎn)換,將 const char * 類型轉(zhuǎn)換為 string 類型后才賦值的。s4 也是類似的道理。
對(duì)于 s1、s2、s3、s4,都是將其它對(duì)象的數(shù)據(jù)拷貝給當(dāng)前對(duì)象,以完成當(dāng)前對(duì)象的初始化。
對(duì)于 func() 的形參 str,其實(shí)在定義時(shí)就為它分配了內(nèi)存,但是此時(shí)并沒(méi)有初始化,只有等到調(diào)用 func() 時(shí),才會(huì)將其它對(duì)象的數(shù)據(jù)拷貝給 str 以完成初始化。
當(dāng)以拷貝的方式初始化一個(gè)對(duì)象時(shí),會(huì)調(diào)用一個(gè)特殊的構(gòu)造函數(shù),就是拷貝構(gòu)造函數(shù)(Copy Constructor)。
下面的例子演示了拷貝構(gòu)造函數(shù)的定義和使用:
#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<<",成績(jī)是"<<m_score<<endl; } int main(){ 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; }
運(yùn)行結(jié)果:
Copy constructor was called.
Copy constructor was called.
小明的年齡是16,成績(jī)是90.5
小明的年齡是16,成績(jī)是90.5
小明的年齡是16,成績(jī)是90.5
第 8 行是拷貝構(gòu)造函數(shù)的聲明,第 20 行是拷貝構(gòu)造函數(shù)的定義??截悩?gòu)造函數(shù)只有一個(gè)參數(shù),它的類型是當(dāng)前類的引用,而且一般都是 const 引用。
1) 為什么必須是當(dāng)前類的引用呢?
如果拷貝構(gòu)造函數(shù)的參數(shù)不是當(dāng)前類的引用,而是當(dāng)前類的對(duì)象,那么在調(diào)用拷貝構(gòu)造函數(shù)時(shí),會(huì)將另外一個(gè)對(duì)象直接傳遞給形參,這本身就是一次拷貝,會(huì)再次調(diào)用拷貝構(gòu)造函數(shù),然后又將一個(gè)對(duì)象直接傳遞給了形參,將繼續(xù)調(diào)用拷貝構(gòu)造函數(shù)……這個(gè)過(guò)程會(huì)一直持續(xù)下去,沒(méi)有盡頭,陷入死循環(huán)。
只有當(dāng)參數(shù)是當(dāng)前類的引用時(shí),才不會(huì)導(dǎo)致再次調(diào)用拷貝構(gòu)造函數(shù),這不僅是邏輯上的要求,也是 C++ 語(yǔ)法的要求。
2) 為什么是 const 引用呢?
拷貝構(gòu)造函數(shù)的目的是用其它對(duì)象的數(shù)據(jù)來(lái)初始化當(dāng)前對(duì)象,并沒(méi)有期望更改其它對(duì)象的數(shù)據(jù),添加 const 限制后,這個(gè)含義更加明確了。
另外一個(gè)原因是,添加 const 限制后,可以將 const 對(duì)象和非 const 對(duì)象傳遞給形參了,因?yàn)榉?const 類型可以轉(zhuǎn)換為 const 類型。如果沒(méi)有 const 限制,就不能將 const 對(duì)象傳遞給形參,因?yàn)?const 類型不能轉(zhuǎn)換為非 const 類型,這就意味著,不能使用 const 對(duì)象來(lái)初始化當(dāng)前對(duì)象了。
以上面的 Student 類為例,將 const 去掉后,拷貝構(gòu)造函數(shù)的原型變?yōu)椋?br />Student::Student(Student &stu);
此時(shí),下面的代碼就會(huì)發(fā)生錯(cuò)誤:
const Student stu1("小明", 16, 90.5); Student stu2 = stu1; Student stu3(stu1);
stu1 是 const 類型,在初始化 stu2、stu3 時(shí),編譯器希望調(diào)用Student::Student(const Student &stu),但是這個(gè)函數(shù)卻不存在,又不能將 const Student 類型轉(zhuǎn)換為 Student 類型去調(diào)用Student::Student(Student &stu),所以最終調(diào)用失敗了。
當(dāng)然,你也可以再添加一個(gè)參數(shù)為 const 引用的拷貝構(gòu)造函數(shù),這樣就不會(huì)出錯(cuò)了。換句話說(shuō),一個(gè)類可以同時(shí)存在兩個(gè)拷貝構(gòu)造函數(shù),一個(gè)函數(shù)的參數(shù)為 const 引用,另一個(gè)函數(shù)的參數(shù)為非 const 引用。
默認(rèn)拷貝構(gòu)造函數(shù)
在前面的教程中,我們還沒(méi)有講解拷貝構(gòu)造函數(shù),但是卻已經(jīng)在使用拷貝的方式創(chuàng)建對(duì)象了,并且也沒(méi)有引發(fā)什么錯(cuò)誤。這是因?yàn)?,如果程序員沒(méi)有顯式地定義拷貝構(gòu)造函數(shù),那么編譯器會(huì)自動(dòng)生成一個(gè)默認(rèn)的拷貝構(gòu)造函數(shù)。這個(gè)默認(rèn)的拷貝構(gòu)造函數(shù)很簡(jiǎn)單,就是使用“老對(duì)象”的成員變量對(duì)“新對(duì)象”的成員變量進(jìn)行一一賦值,和上面 Student 類的拷貝構(gòu)造函數(shù)非常類似。
對(duì)于簡(jiǎn)單的類,默認(rèn)拷貝構(gòu)造函數(shù)一般是夠用的,我們也沒(méi)有必要再顯式地定義一個(gè)功能類似的拷貝構(gòu)造函數(shù)。但是當(dāng)類持有其它資源時(shí),如動(dòng)態(tài)分配的內(nèi)存、打開(kāi)的文件、指向其他數(shù)據(jù)的指針、網(wǎng)絡(luò)連接等,默認(rèn)拷貝構(gòu)造函數(shù)就不能拷貝這些資源,我們必須顯式地定義拷貝構(gòu)造函數(shù),以完整地拷貝對(duì)象的所有數(shù)據(jù)。
總結(jié)
到此這篇關(guān)于C++中的拷貝構(gòu)造函數(shù)詳解的文章就介紹到這了,更多相關(guān)C++拷貝構(gòu)造函數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言使用setjmp和longjmp實(shí)現(xiàn)一個(gè)簡(jiǎn)單的協(xié)程
這篇文章主要為大家介紹了C語(yǔ)言使用setjmp和longjmp實(shí)現(xiàn)一個(gè)簡(jiǎn)單的協(xié)程過(guò)程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12C語(yǔ)言結(jié)構(gòu)體嵌套與對(duì)齊超詳細(xì)講解
這篇文章主要介紹了C語(yǔ)言結(jié)構(gòu)體嵌套與對(duì)齊,C語(yǔ)言中結(jié)構(gòu)體是一種構(gòu)造類型,和數(shù)組、基本數(shù)據(jù)類型一樣,可以定義指向該種類型的指針。結(jié)構(gòu)體指針的定義類似其他基本數(shù)據(jù)類型的定義2022-12-12opencv車(chē)道線檢測(cè)的實(shí)現(xiàn)方法
這篇文章主要介紹了opencv車(chē)道線檢測(cè)的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08C語(yǔ)言實(shí)現(xiàn)的排列組合問(wèn)題的通用算法、解決方法
這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)的排列組合問(wèn)題的通用算法、解決方法,本文使用C語(yǔ)言實(shí)現(xiàn)在程序中解決這個(gè)問(wèn)題,需要的朋友可以參考下2014-08-08