<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
拷貝和複製是一個意思,對應的英文單詞都是copy。對於計算機來說,拷貝是指用一份原有的、已經存在的資料建立出一份新的資料,最終的結果是多了一份相同的資料。例如,將 Word 檔案拷貝到U盤去影印店列印,將 D 盤的圖片拷貝到桌面以方便瀏覽,將重要的檔案上傳到百度網路硬碟以防止丟失等,都是「建立一份新資料」的意思。
在 C++ 中,拷貝並沒有脫離它本來的含義,只是將這個含義進行了“特化”,是指用已經存在的物件建立出一個新的物件。從本質上講,物件也是一份資料,因為它會佔用記憶體。
嚴格來說,物件的建立包括兩個階段,首先要分配記憶體空間,然後再進行初始化:
分配記憶體很好理解,就是在堆區、棧區或者全域性資料區留出足夠多的位元組。這個時候的記憶體還比較“原始”,沒有被“教化”,它所包含的資料一般是零值或者隨機值,沒有實際的意義。
初始化就是首次對記憶體賦值,讓它的資料有意義。注意是首次賦值,再次賦值不叫初始化。初始化的時候還可以為物件分配其他的資源(開啟檔案、連線網路、動態分配記憶體等),或者提前進行一些計算(根據價格和數量計算出總價、根據長度和寬度計算出矩形的面積等)等。說白了,初始化就是呼叫建構函式。
很明顯,這裡所說的拷貝是在初始化階段進行的,也就是用其它物件的資料來初始化新物件的記憶體。
那麼,如何用拷貝的方式來初始化一個物件呢?其實這樣的例子比比皆是,string 類就是一個典型的例子。
#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; } 執行結果: 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,都是使用拷貝的方式來初始化的。
對於 s1,表面上看起來是將一個字串直接賦值給了 s1,實際上在內部進行了型別轉換,將 const char * 型別轉換為 string 型別後才賦值的。s4 也是類似的道理。
對於 s1、s2、s3、s4,都是將其它物件的資料拷貝給當前物件,以完成當前物件的初始化。
對於 func() 的形參 str,其實在定義時就為它分配了記憶體,但是此時並沒有初始化,只有等到呼叫 func() 時,才會將其它物件的資料拷貝給 str 以完成初始化。
當以拷貝的方式初始化一個物件時,會呼叫一個特殊的建構函式,就是拷貝建構函式(Copy Constructor)。
下面的例子演示了拷貝建構函式的定義和使用:
#include <iostream> #include <string> using namespace std; class Student{ public: Student(string name = "", int age = 0, float score = 0.0f); //普通建構函式 Student(const Student &stu); //拷貝建構函式(宣告) 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){ } //拷貝建構函式(定義) 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(){ Student stu1("小明", 16, 90.5); Student stu2 = stu1; //呼叫拷貝建構函式 Student stu3(stu1); //呼叫拷貝建構函式 stu1.display(); stu2.display(); stu3.display(); return 0; }
執行結果:
Copy constructor was called.
Copy constructor was called.
小明的年齡是16,成績是90.5
小明的年齡是16,成績是90.5
小明的年齡是16,成績是90.5
第 8 行是拷貝建構函式的宣告,第 20 行是拷貝建構函式的定義。拷貝建構函式只有一個引數,它的型別是當前類的參照,而且一般都是 const 參照。
如果拷貝建構函式的引數不是當前類的參照,而是當前類的物件,那麼在呼叫拷貝建構函式時,會將另外一個物件直接傳遞給形參,這本身就是一次拷貝,會再次呼叫拷貝建構函式,然後又將一個物件直接傳遞給了形參,將繼續呼叫拷貝建構函式……這個過程會一直持續下去,沒有盡頭,陷入死迴圈。
只有當引數是當前類的參照時,才不會導致再次呼叫拷貝建構函式,這不僅是邏輯上的要求,也是 C++ 語法的要求。
拷貝建構函式的目的是用其它物件的資料來初始化當前物件,並沒有期望更改其它物件的資料,新增 const 限制後,這個含義更加明確了。
另外一個原因是,新增 const 限制後,可以將 const 物件和非 const 物件傳遞給形參了,因為非 const 型別可以轉換為 const 型別。如果沒有 const 限制,就不能將 const 物件傳遞給形參,因為 const 型別不能轉換為非 const 型別,這就意味著,不能使用 const 物件來初始化當前物件了。
以上面的 Student 類為例,將 const 去掉後,拷貝建構函式的原型變為:
Student::Student(Student &stu);
此時,下面的程式碼就會發生錯誤:
const Student stu1("小明", 16, 90.5); Student stu2 = stu1; Student stu3(stu1);
stu1 是 const 型別,在初始化 stu2、stu3 時,編譯器希望呼叫Student::Student(const Student &stu),但是這個函數卻不存在,又不能將 const Student 型別轉換為 Student 型別去呼叫Student::Student(Student &stu),所以最終呼叫失敗了。
當然,你也可以再新增一個引數為 const 參照的拷貝建構函式,這樣就不會出錯了。換句話說,一個類可以同時存在兩個拷貝建構函式,一個函數的引數為 const 參照,另一個函數的引數為非 const 參照。
在前面的教學中,我們還沒有講解拷貝建構函式,但是卻已經在使用拷貝的方式建立物件了,並且也沒有引發什麼錯誤。這是因為,如果程式設計師沒有顯式地定義拷貝建構函式,那麼編譯器會自動生成一個預設的拷貝建構函式。這個預設的拷貝建構函式很簡單,就是使用“老物件”的成員變數對“新物件”的成員變數進行一一賦值,和上面 Student 類的拷貝建構函式非常類似。
對於簡單的類,預設拷貝建構函式一般是夠用的,我們也沒有必要再顯式地定義一個功能類似的拷貝建構函式。但是當類持有其它資源時,如動態分配的記憶體、開啟的檔案、指向其他資料的指標、網路連線等,預設拷貝建構函式就不能拷貝這些資源,我們必須顯式地定義拷貝建構函式,以完整地拷貝物件的所有資料。
到此這篇關於C++中的拷貝建構函式詳解的文章就介紹到這了,更多相關C++拷貝建構函式內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45