淺談C++類(6)--復制構造函數
還記得(1)中講到的構造函數嗎?復習一下,這次我們重載一個新的默認構造函數--即當你不給出初始值時調用的構造函數,我記得我講過這個概念吧,有嗎?看下面的例子。
例6.0
#include
#include
using namespace std;
class Fruit?????????????? //定義一個類,名字叫Fruit
{
string name;???? //定義一個name成員
string colour;?? //定義一個colour成員
public:
void print()????????????? //定義一個輸出名字的成員print()
{
cout< } Fruit(const string &nst,const string &cst = "green"):name(nst),colour(cst)? //構造函數 { name +="s"; } Fruit(istream &is = cin)?? //新的構造函數 { is>>colour>>name; } }; int main() { Fruit apple("apple");? //定義一個Fruit類對象apple Fruit apple2; apple.print(); apple2.print(); return 0; } 發現我重載的默認構造函數沒有?這次利用的是默認形參(istream &is =cin),學過io的就應該知道,他的意思表示,默認就是從標準設備輸入(如鍵盤)。你運行下,就知道怎么回事了。現在我們講一個新內容,復制構造函數,什么意思?先看下面的例子。 例6.1: #include #include using namespace std; class Fruit?????????????? //定義一個類,名字叫Fruit { string name;???? //定義一個name成員 string colour;?? //定義一個colour成員 public: void print()????????????? //定義一個輸出名字的成員print() { cout< } Fruit(const string &nst,const string &cst = "green"):name(nst),colour(cst)? //構造函數 { name +="s"; } Fruit(){} }; int main() { Fruit apple("apple");? //定義一個Fruit類對象apple Fruit apple2(apple);//發現這里有什么問題沒有? apple.print(); apple2.print(); return 0; } 你會發現apple2也輸出了green apples,為什么啊?(apple)和("apple")一樣?你這這樣理解可就錯了,肯定不一樣嘛。但是當我們使用Fruit apple2(apple);的時候調用了哪個構造函數呢?我們沒有定義一個類似的構造函數啊?按道理應該編譯失敗,不是嗎?恩,這里調用的構造函數就叫做復制構造函數,即用一個同樣類型的對象構造另一個對象的構造函數,不過在這里,我們沒有定義,所以由系統幫我們自動定義的,叫做默認復制構造函數。效果自然就是復制一下。你把第一個對象改成apple3你就會發現,apple2沒有辦法定義了,因為它調用的是復制Fruit對象apple的構造函數,而不是用字符串"apple"那個構造函數。C++ Primer這樣定義復制構造函數,我引用一下“只有單個形參,而且該形參是對本類類型對象的引用(常用const修飾)”。我們來看看系統合成的默認復制構造函數的一個有趣應用: 例6.2: #include using namespace std; class Aint { public: int aival[3]; }; int main() { Aint as={1,2,3}; cout< Aint bs(as); cout< return 0; } 很簡單的例子吧,不過也很有趣,我們都知道,數組是沒有辦法通過等于來復制的,要復制只能利用循環遍歷,我們自己定義了一個只包含整形數組的類,而當我們利用系統合成的默認復制構造函數的時候實現了數組的復制,注意,是一次性等于復制。呵呵。這也說明了一個問題,就是系統的默認復制構造函數在對付數組時,幫我們遍歷復制了。現在我們自己定義一個復制構造函數。要說明的是,一般情況下系統定義的復制構造函數已經夠用了,當你自己要定義的時候是想實現不同的功能,比如更好的處理指針的復制等,下面的例子只是看看用法,我也只講用法而不講究有沒有實際意義。 例6.3: #include #include using namespace std; class Fruit?????????????? //定義一個類,名字叫Fruit { string name;???? //定義一個name成員 string colour;?? //定義一個colour成員 public: void print()????????????? //定義一個輸出名字的成員print() { cout< } Fruit(const string &nst = "apple",const string &cst = "green"):name(nst),colour(cst){}? //構造函數 Fruit(Fruit &aF):name(aF.name),colour(aF.colour)??????????? //這是我們自己定義的復制構造函數 { name +="s";??????????????? //讓他和默認的不同 } }; int main() { Fruit apple;? //定義一個Fruit類對象apple Fruit apple2(apple);//調用的是我們自己定義的復制構造函數 apple.print(); apple2.print();????????? //你會發現輸出多了個's' return 0; } 這里你會看到我們自己定義的復制構造函數的作用,直觀的看到apple只輸出green apple,而apple2輸出green aples,要說明的是,這也是復制構造函數也是構造函數,也可以用初始化列表,而且在C++ Primer中還推薦你使用初始化列表。下面我們看看,假如你向讓你的類禁止復制怎么辦啊?很簡單,讓你的復制構造函數跑到private里面去,這時候友元和成員還可以使用復制,那你就光聲明一個復制構造函數,但是,你不定義它,在C++里面,光聲明不定義一個成員函數是合法的,但是,使用的話就會導致編譯失敗了,(普通函數也是這樣)通過這種手段,你就能禁止一切復制的發生了(其實是發現一切本需要復制構造函數的地方了)。見下例。 例6.4: #include #include using namespace std; class Fruit?????????????? //定義一個類,名字叫Fruit { string name;???? //定義一個name成員 string colour;?? //定義一個colour成員 public: void print()????????????? //定義一個輸出名字的成員print() { cout< } Fruit(const string &nst = "apple",const string &cst = "green"):name(nst),colour(cst) {}? //構造函數 private: Fruit(Fruit &aF);??????? //把它定義在private下 }; int main() { Fruit apple("apple");? //定義一個Fruit類對象apple //?Fruit apple2(apple);?? //你這樣的嘗試會導致編譯失敗的,cannot access private 錯誤 apple.print(); return 0; } 在犯了一個我半天也沒有發現的錯誤的后,我發現了,當利用形如Fruit apple2 = apple方式來定義并初始化一個對象的時候,調用的也是復制構造函數,詳情請見那個帖子《警惕!C++里面“=”不一定就是等于(賦值)。 》 C++
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。