C++之構(gòu)造函數(shù)
說明: 從CSDN看到兩篇很好的文章,感覺寫的很好,,特別這幾個(gè)例子舉的好,,轉(zhuǎn)載下來備忘 出處: http://blog.csdn.net/vagrxie/archive/2007/04/25/1584756.aspx http://blog.csdn.net/vagrxie/archive/2007/04/27/1587178.aspx http://blog.csdn.net/vagrxie/archive/2007/04/28/1588984.aspx 作者:九天雁翎 -------------------------------------------------------------------------------------------------------*/ 一、構(gòu)造函數(shù)
類多么重要我就不多說了,,只講講學(xué)習(xí),,因?yàn)閭€(gè)人認(rèn)為類的學(xué)習(xí)無論從概念的理解還是實(shí)際代碼的編寫相對其他C兼容向的代碼都是比較有難度的,, 對于以前學(xué)C 的人來說這才是真正的新概念和內(nèi)容,STL其實(shí)還比較好理解,,不就是一個(gè)更大的函數(shù)庫和代碼可以使用嘛,。雖然vector,string就是類,不過我們卻不需要這樣去理解他們,,就可以很好的使用了,。
先說明,1,,這是非常初級(jí)的東西,。2,你懂了就不需要看了,。3,,我寫出來是幫助還不懂得人。4,,我自己也還不太懂,,所以才寫下來,梳理一下,,希望自己能更好的理解,,因?yàn)槲蚁嘈乓痪湓挘芎玫睦斫庖粋€(gè)東西的好方法是把這個(gè)東西教給別人,。有什么不對的地方,,歡迎指出,我非常感謝,,還有很多時(shí)候,,某種方法是不允許的,了解也很重要,,但我不想給出錯(cuò)誤的例子,,那樣很容易給出誤導(dǎo),只講這樣是錯(cuò)誤的,,希望你可以自己輸入去嘗試一下,,看看得出的是什么錯(cuò)誤。
一,、概念:就Bjarne Stroustup自己說,,來自于Simula的概念(The Design and Evolution of C++),我不懂Simula,,所以,,還是對我沒有什么幫助,基本上,都說類是具體對象(實(shí)例)的抽象,,怎么抽象,?就是把一個(gè)實(shí)例的特征拿出來,,比如,,水果是一個(gè)類,蘋果就是一個(gè)實(shí)例,,蘋果有水果的特征,。我們只要從蘋果香蕉中把特征抽象出來“class Fruits{ }”;就好了,。然后 “Fruits apple”,,表示蘋果是一個(gè)水果。就像人是一個(gè)類的話,,我們就都是實(shí)例,。一下也講不清,不過也可以從另一個(gè)角度去理解,,就是Bjarne Stroustup自己說的,,一個(gè)Class其實(shí)就是一個(gè)用戶定義的新Type,這點(diǎn)上和Struct沒有什么本質(zhì)上的區(qū)別,,只是使用上的區(qū)別而已,。之所以沒有把它直接叫作Type是因?yàn)樗囊粋€(gè)不定義新名字的原則。
二,、使用:我一直覺得比較惱火,,光看概念是沒有用的,學(xué)習(xí)程序,,自己編寫代碼是最快的,。下面是幾個(gè)步驟:
1:最簡單的一個(gè)類。
C++中使用任何東西都要先定義吧,,類也不例外,。用水果舉例,水果的特征最起碼的名字先這1個(gè)吧,。名字用string表示,。
例1.0:
#include <string> #include <iostream> using namespace std; class Fruit //定義一個(gè)類,名字叫Fruit { public: //標(biāo)號(hào),,表示這個(gè)類成員可以在外部訪問 string name; }; int main() { Fruit apple = {"apple"}; //定義一個(gè)Fruit類對象apple cout<< apple.name<<endl; //使用apple的成員name return 0; }
在這里說明,,以后其他細(xì)節(jié)我都省略說明了,比如#include,using,cout等等,,先去學(xué)會(huì)吧,。我只說類;你會(huì)發(fā)現(xiàn)其實(shí)在這里把class換成struct沒有任何問題,,的確,,而且換成sturct后"public:" 標(biāo)號(hào)都可以省略,,記住,在C++里面,,struct與class其實(shí)沒有本質(zhì)的區(qū)別,,只是stuct默認(rèn)成員為public而class默認(rèn)為private。public顧名思義,,就是公共的,,誰都可以訪問,private自然就是私人的,,別人就不能訪問了,,你把例1.0的public:標(biāo)號(hào)這行去掉試試。你會(huì)得到兩個(gè)錯(cuò)誤,,1,,不能通過 Fruit apple = {"apple"};形式定義,2,,cout<<行不能訪問私有成員,。這里class幾乎就和c里面的struct使用沒有區(qū)別,包括apple.name點(diǎn)操作符表示使用對象apple里面的一個(gè)成員,,還有Fruit apple = {"apple"};這樣的定義初始化方法,。很好理解吧,不多說了,。說點(diǎn)不同的,,C++里面class(struct)不僅可以有數(shù)據(jù)成員,也可以有函數(shù)成員,。比如,,我們希望類Fruit可以自己輸出它的名字,而不是我們從外部訪問成員,。
例1.1:
#include <string> #include <iostream> using namespace std; class Fruit //定義一個(gè)類,,名字叫Fruit { public: //標(biāo)號(hào),表示這個(gè)類成員可以在外部訪問 string name; //定義一個(gè)name成員 void print() //定義一個(gè)輸出名字的成員print() { cout<< name<<endl; } }; int main() { Fruit apple = {"apple"}; //定義一個(gè)Fruit類對象apple apple.print(); //使用apple的成員print return 0; }
這里你會(huì)發(fā)現(xiàn)與C的不同,,而這看起來一點(diǎn)點(diǎn)地不同,,即可以在class(struct)中添加函數(shù)成員,讓C++有了面向?qū)ο筇卣?,而C只能是結(jié)構(gòu)化編程(這在C剛出來的時(shí)候也是先進(jìn)的代表,,不過卻不代表現(xiàn)在的先進(jìn)編程方法)。還有,,你發(fā)現(xiàn)定義函數(shù)成員和定義普通函數(shù)語法是一樣的,,使用上和普通成員使用也一樣。再進(jìn)一步,在C++中有構(gòu)造函數(shù)的概念,先看例子
例1.2:
#include <string> #include <iostream> using namespace std; class Fruit //定義一個(gè)類,,名字叫Fruit { public: //標(biāo)號(hào),,表示這個(gè)類成員可以在外部訪問 string name; //定義一個(gè)name成員 void print() //定義一個(gè)輸出名字的成員print() { cout<< name<<endl; } Fruit(const string &st) //定義一個(gè)函數(shù)名等于類名的函數(shù)成員 { name = st; }
}; int main() { Fruit apple = Fruit("apple"); //定義一個(gè)Fruit類對象apple Fruit orange("orange"); apple.print(); //使用apple的成員print orange.print(); return 0; }
例子1.2里面的函數(shù)名等于類名的函數(shù)成員就叫作構(gòu)造函數(shù),在每次你定義一個(gè)新對象的時(shí)候,,程序自動(dòng)調(diào)用,,這里,定義了2個(gè)對象,,一個(gè)apple, 一個(gè)orange,,分別用了2種不同的方法,你會(huì)發(fā)現(xiàn)構(gòu)造函數(shù)的作用,,這里,要說的是,,假如你還按以前的方法Fruit apple = {"apple"}定義apple你會(huì)編譯失敗,,因?yàn)橛辛藰?gòu)造函數(shù)了,F(xiàn)ruit apple就定義成功了一個(gè)對象,,讓apple對象等于{"apple"}的使用是不允許的,。對象只能等于對象,所以你可以先用Fruit("apple")構(gòu)造一個(gè)臨時(shí)的對象,,然后讓apple等于它,。orange對象的定義就更好理解了,直接調(diào)用構(gòu)造函數(shù)嘛,。這里要說的是,,你不可以直接Fruit banana了,因?yàn)闆]有可以用的構(gòu)造函數(shù),,而沒有用構(gòu)造函數(shù)前,,你是可以這樣做的。直接Fruit apple,,再使apple.name = "apple",,是完全可以的。
例1.3:
#include <string> #include <iostream> using namespace std; class Fruit //定義一個(gè)類,,名字叫Fruit { public: //標(biāo)號(hào),,表示這個(gè)類成員可以在外部訪問 string name; //定義一個(gè)name成員 void print() //定義一個(gè)輸出名字的成員print() { cout<< name<<endl; }
}; int main() { Fruit apple; //定義一個(gè)Fruit類對象apple apple.name ="apple"; //這時(shí)候才初始化apple的成員name apple.print(); //使用apple的成員print return 0; }
而有了構(gòu)造函數(shù)以后就不能這樣了,怎么樣不失去這種靈活性呢,?你有兩種辦法,。其一是重載一個(gè)空的構(gòu)造函數(shù),記得,,構(gòu)造函數(shù)也是一個(gè)函數(shù),,自然也可以重載羅。你還不知道什么是重載?那先去學(xué)這個(gè)簡單的東西吧,,類比那家伙復(fù)雜太多了,。
例1.4:
#include <string> #include <iostream> using namespace std; class Fruit //定義一個(gè)類,名字叫Fruit { public: //標(biāo)號(hào),,表示這個(gè)類成員可以在外部訪問 string name; //定義一個(gè)name成員 void print() //定義一個(gè)輸出名字的成員print() { cout<< name<<endl; } Fruit(const string &st) { name = st; } Fruit(){} //重載一個(gè)空構(gòu)造函數(shù) }; int main() { Fruit apple; //定義一個(gè)Fruit類對象apple,這時(shí)是允許的了,,自動(dòng)調(diào)用第2個(gè)構(gòu)造函數(shù) apple.name ="apple"; //這時(shí)候才初始化apple的成員name apple.print(); //使用apple的成員print return 0; }
第二種辦法,就是使用構(gòu)造函數(shù)默認(rèn)實(shí)參,;
例1.5
#include <string> #include <iostream> using namespace std; class Fruit //定義一個(gè)類,,名字叫Fruit { public: //標(biāo)號(hào),表示這個(gè)類成員可以在外部訪問 string name; //定義一個(gè)name成員 void print() //定義一個(gè)輸出名字的成員print() { cout<< name<<endl; } Fruit(const string &st = "banana") { name = st; } }; int main() { Fruit apple; //定義一個(gè)Fruit類對象apple apple.print(); apple.name ="apple"; //這時(shí)候才初始化apple的成員name apple.print(); //使用apple的成員print return 0; }
這個(gè)程序里面,,當(dāng)你直接定義一個(gè)無初始化值的apple對象時(shí),,你發(fā)現(xiàn),他直接把name表示為banana,。也許現(xiàn)在你會(huì)問,,為什么需要構(gòu)造函數(shù)呢?這里解釋以前留下來的問題,。即不推介使用Fruit apple = {"apple"}的原因,。因?yàn)檫@樣初始化,你必須要保證成員可以訪問,,當(dāng)name為私有的時(shí)候,,這樣可就不奏效了,為什么需要私有呢,?這就牽涉到類的數(shù)據(jù)封裝問題,,類有不希望別人訪問的成員,以防破壞內(nèi)部的完整性,,也以防誤操作,。這點(diǎn)上要講就很復(fù)雜了,不多講了,。只講操作吧,。
例1.6
#include <string> #include <iostream> using namespace std; class Fruit //定義一個(gè)類,名字叫Fruit { //沒有標(biāo)號(hào)了,,表示這個(gè)類成員不可以在外部訪問,,class默認(rèn)為private哦 string name; //定義一個(gè)name私有成員 public: void print() //定義一個(gè)輸出名字的成員print() { cout<< name<<endl; } Fruit(const string &st = "banana") { name = st; } }; int main() { Fruit banana; //定義一個(gè)Fruit類對象
banana.print(); // banana.name ="apple"; //這時(shí)候才改變banana的成員name已經(jīng)是不允許的了 // 你要定義一個(gè)name等于apple的成員必須這樣: Fruit apple("apple"); apple.print(); return 0; }
要說明的是,構(gòu)造函數(shù)你必須定義成公用的啊,,因?yàn)槟惚仨氁谕獠空{(diào)用啊?,F(xiàn)在講講構(gòu)造函數(shù)特有的形式,初始化列表,,這點(diǎn)和一般的函數(shù)不一樣,。
例1.7:
#include <string> #include <iostream> using namespace std; class Fruit //定義一個(gè)類,,名字叫Fruit { string name; //定義一個(gè)name成員 public: void print() //定義一個(gè)輸出名字的成員print() { cout<< name<<endl; } Fruit(const string &st = "banana"):name(st){} //看到不同了嗎? }; int main() { Fruit banana; //定義一個(gè)Fruit類對象
banana.print(); return 0; }
在參數(shù)表后,,函數(shù)實(shí)體前,,以“:”開頭,列出的一個(gè)列表,,叫初始化列表,,這里初始化列表的作用和以前的例子完全一樣,就是用st初始化name,,問題是,,為什么要特別定義這個(gè)東西呢?C++ Primer的作者Lippman在書里面聲稱這時(shí)許多相當(dāng)有經(jīng)驗(yàn)的C++程序員都沒有掌握的一個(gè)特性,,因?yàn)楹芏鄷r(shí)候根本就不需要,,用我們以前的形式就夠了但有種情況是例外。在說明前我們?yōu)槲覀兊腇ruit加個(gè)固定新成員,,而且定義后不希望再改變了,,比如顏色。
例1.8:
#include <string> #include <iostream> using namespace std; class Fruit //定義一個(gè)類,,名字叫Fruit { string name; //定義一個(gè)name成員 const string colour; public: void print() //定義一個(gè)輸出名字的成員print() { cout<<colour<<" "<<name<<endl; } Fruit(const string &nst = "apple",const string &cst = "green"):name(nst),colour(cst){} }; int main() { Fruit apple; //定義一個(gè)Fruit類對象apple apple.print(); return 0; }
在這里你把colour的初始化放到{}里面,用以前的那種方法,,你會(huì)發(fā)現(xiàn)編譯錯(cuò)誤,,因?yàn)樗莄onst的,而實(shí)際上放在{}里面是個(gè)計(jì)算階段,,而放在初始化列表里面就可以,,因?yàn)槌跏蓟斜淼氖褂檬窃跀?shù)據(jù)定義的時(shí)候就自動(dòng)調(diào)用了,因?yàn)檫@個(gè)原因,,數(shù)據(jù)的調(diào)用順序和初始化列表里面的順序無關(guān),,只和數(shù)據(jù)定義的順序有關(guān),給兩個(gè)例子,,比如你在上面的例子中把初始化列表改為":colour(name),name(nst)"沒有任何問題,,因?yàn)樵诙xcolour前面,name 就已經(jīng)定義了,,但是":name(colour),colour(cst)"卻不行,,因?yàn)樵趎ame定義的時(shí)候colour還沒有被定義,而且問題的嚴(yán)重性在于我可以通過編譯.........太嚴(yán)重了,,所以在C++ Primer不推薦你使用數(shù)據(jù)成員初始化另外一個(gè)數(shù)據(jù),,有需要的話,可以":name(cst),colour(cst)",,一樣的效果,。另外,,初始化列表在定義時(shí)就自動(dòng)調(diào)用了,所以在構(gòu)造函數(shù){}之前使用,,你可以看看這個(gè)例子:
例1.9 :
#include <string> #include <iostream> using namespace std; class Fruit //定義一個(gè)類,,名字叫Fruit { string name; //定義一個(gè)name成員 const string colour; public: void print() //定義一個(gè)輸出名字的成員print() { cout<<colour<<" "<<name<<endl; } Fruit(const string &nst = "apple",const string &cst = "green"):name(nst),colour(cst) { name +="s"; //這時(shí)name已經(jīng)等于"apple“了 } }; int main() { Fruit apple("apple","red"); //定義一個(gè)Fruit類對象apple apple.print(); return 0; }
最后輸出red apples。先講到這里吧,,你明白一點(diǎn)什么是類沒有,?像我一樣學(xué)了老半天還不明白的,堅(jiān)持住,,多練習(xí),,總能明白的。我現(xiàn)在似乎明白的多一點(diǎn)了:)
//--------------------------------------------------------------------------------------------------------------- //1看完了,,特別是例1.8不錯(cuò)呀 //還有些郁悶,,對復(fù)制構(gòu)造函數(shù)的體會(huì)不深,于是看了作者的第6篇 //---------------------------------------------------------------------------------------------------------------- 二,、復(fù)制構(gòu)造函數(shù)
還記得(1)中講到的構(gòu)造函數(shù)嗎,?復(fù)習(xí)一下,這次我們重載一個(gè)新的默認(rèn)構(gòu)造函數(shù)--即當(dāng)你不給出初始值時(shí)調(diào)用的構(gòu)造函數(shù),,我記得我講過這個(gè)概念吧,,有嗎?看下面的例子,。
例6.0
#include <string> #include <iostream> using namespace std; class Fruit //定義一個(gè)類,,名字叫Fruit { string name; //定義一個(gè)name成員 string colour; //定義一個(gè)colour成員 public: void print() //定義一個(gè)輸出名字的成員print() { cout<<colour<<" "<<name<<endl; } Fruit(const string &nst,const string &cst = "green"):name(nst),colour(cst) //構(gòu)造函數(shù) { name +="s"; } Fruit(istream &is = cin) //新的構(gòu)造函數(shù) { is>>colour>>name; } }; int main() { Fruit apple("apple"); //定義一個(gè)Fruit類對象apple Fruit apple2; apple.print(); apple2.print(); return 0; }
發(fā)現(xiàn)我重載的默認(rèn)構(gòu)造函數(shù)沒有?這次利用的是默認(rèn)形參(istream &is =cin),,學(xué)過io的就應(yīng)該知道,,他的意思表示,默認(rèn)就是從標(biāo)準(zhǔn)設(shè)備輸入(如鍵盤),。你運(yùn)行下,,就知道怎么回事了。現(xiàn)在我們講一個(gè)新內(nèi)容,,復(fù)制構(gòu)造函數(shù),,什么意思?先看下面的例子,。
例6.1:
#include <string> #include <iostream> using namespace std; class Fruit //定義一個(gè)類,,名字叫Fruit { string name; //定義一個(gè)name成員 string colour; //定義一個(gè)colour成員 public: void print() //定義一個(gè)輸出名字的成員print() { cout<<colour<<" "<<name<<endl; } Fruit(const string &nst,const string &cst = "green"):name(nst),colour(cst) //構(gòu)造函數(shù) { name +="s"; } Fruit(){} }; int main() { Fruit apple("apple"); //定義一個(gè)Fruit類對象apple Fruit apple2(apple);//發(fā)現(xiàn)這里有什么問題沒有? apple.print(); apple2.print(); return 0; }
你會(huì)發(fā)現(xiàn)apple2也輸出了green apples,,為什么?。浚╝pple)和("apple")一樣,?你這這樣理解可就錯(cuò)了,,肯定不一樣嘛,。但是當(dāng)我們使用Fruit apple2(apple);的時(shí)候調(diào)用了哪個(gè)構(gòu)造函數(shù)呢?我們沒有定義一個(gè)類似的構(gòu)造函數(shù)???按道理應(yīng)該編譯失敗,不是嗎,?恩,,這里調(diào)用的構(gòu)造函數(shù)就叫 做復(fù)制構(gòu)造函數(shù),即用一個(gè)同樣類型的對象構(gòu)造另一個(gè)對象的構(gòu)造函數(shù),不過在這里,,我們沒有定義,,所以由系統(tǒng)幫我們自動(dòng)定義的,叫做默認(rèn)復(fù)制構(gòu)造函數(shù),。效果 自然就是復(fù)制一下,。你把第一個(gè)對象改成apple3你就會(huì)發(fā)現(xiàn),apple2沒有辦法定義了,,因?yàn)樗{(diào)用的是復(fù)制Fruit對象apple的構(gòu)造函數(shù),,而 不是用字符串"apple"那個(gè)構(gòu)造函數(shù)。C++ Primer這樣定義復(fù)制構(gòu)造函數(shù),,我引用一下“只有單個(gè)形參,,而且該形參是對本類類型對象的引用(常用const修飾)”。我們來看看系統(tǒng)合成的默認(rèn)復(fù) 制構(gòu)造函數(shù)的一個(gè)有趣應(yīng)用:
例6.2:
#include <iostream> using namespace std; class Aint { public: int aival[3]; }; int main() { Aint as={1,2,3}; cout<<as.aival[0]<<as.aival[1]<<as.aival[2]<<endl; Aint bs(as); cout<<bs.aival[0]<<bs.aival[1]<<bs.aival[2]<<endl; return 0; }
很簡單的例子吧,,不過也很有趣,,我們都知道,數(shù)組是沒有辦法通過等于來復(fù)制的,,要復(fù)制只能利用循環(huán)遍歷,我們自己定義了一個(gè)只包含整形數(shù)組的類,,而 當(dāng)我們利用系統(tǒng)合成的默認(rèn)復(fù)制構(gòu)造函數(shù)的時(shí)候?qū)崿F(xiàn)了數(shù)組的復(fù)制,,注意,是一次性等于復(fù)制,。呵呵,。這也說明了一個(gè)問題,就是系統(tǒng)的默認(rèn)復(fù)制構(gòu)造函數(shù)在對付數(shù) 組時(shí),,幫我們遍歷復(fù)制了?,F(xiàn)在我們自己定義一個(gè)復(fù)制構(gòu)造函數(shù)。要說明的是,,一般情況下系統(tǒng)定義的復(fù)制構(gòu)造函數(shù)已經(jīng)夠用了,,當(dāng)你自己要定義的時(shí)候是想實(shí)現(xiàn)不 同的功能,比如更好的處理指針的復(fù)制等,,下面的例子只是看看用法,,我也只講用法而不講究有沒有實(shí)際意義,。
例6.3:
#include <string> #include <iostream> using namespace std; class Fruit //定義一個(gè)類,名字叫Fruit { string name; //定義一個(gè)name成員 string colour; //定義一個(gè)colour成員 public: void print() //定義一個(gè)輸出名字的成員print() { cout<<colour<<" "<<name<<endl; } Fruit(const string &nst = "apple",const string &cst = "green"):name(nst),colour(cst){} //構(gòu)造函數(shù) Fruit(Fruit &aF):name(aF.name),colour(aF.colour) //這是我們自己定義的復(fù)制構(gòu)造函數(shù) { name +="s"; //讓他和默認(rèn)的不同 } };
int main() { Fruit apple; //定義一個(gè)Fruit類對象apple Fruit apple2(apple);//調(diào)用的是我們自己定義的復(fù)制構(gòu)造函數(shù)
apple.print(); apple2.print(); //你會(huì)發(fā)現(xiàn)輸出多了個(gè)'s' return 0;
}
這里你會(huì)看到我們自己定義的復(fù)制構(gòu)造函數(shù)的作用,,直觀的看到apple只輸出green apple,而apple2輸出green aples,,要說明的是,這也是復(fù)制構(gòu)造函數(shù)也是構(gòu)造函數(shù),,也可以用初始化列表,,而且在C++ Primer中還推薦你使用初始化列表。下面我們看看,,假如你向讓你的類禁止復(fù)制怎么辦?。亢芎唵?,讓你的復(fù)制構(gòu)造函數(shù)跑到private里面去,,這時(shí)候 友元和成員還可以使用復(fù)制,那你就光聲明一個(gè)復(fù)制構(gòu)造函數(shù),,但是,,你不定義它,在C++里面,,光聲明不定義一個(gè)成員函數(shù)是合法的,,但是,使用的話就會(huì)導(dǎo)致 編譯失敗了,,(普通函數(shù)也是這樣)通過這種手段,,你就能禁止一切復(fù)制的發(fā)生了(其實(shí)是發(fā)現(xiàn)一切本需要復(fù)制構(gòu)造函數(shù)的地方了)。見下例,。
例6.4:
#include <string> #include <iostream> using namespace std; class Fruit //定義一個(gè)類,,名字叫Fruit { string name; //定義一個(gè)name成員 string colour; //定義一個(gè)colour成員 public: void print() //定義一個(gè)輸出名字的成員print() { cout<<colour<<" "<<name<<endl; } Fruit(const string &nst = "apple",const string &cst = "green"):name(nst),colour(cst) {} //構(gòu)造函數(shù) private: Fruit(Fruit &aF); //把它定義在private下 }; int main() { Fruit apple("apple"); //定義一個(gè)Fruit類對象apple // Fruit apple2(apple); //你這樣的嘗試會(huì)導(dǎo)致編譯失敗的,cannot access private 錯(cuò)誤 apple.print(); return 0; }
在犯了一個(gè)我半天也沒有發(fā)現(xiàn)的錯(cuò)誤的后,,我發(fā)現(xiàn)了,,當(dāng)利用形如Fruit apple2 = apple方式來定義并初始化一個(gè)對象的時(shí)候,調(diào)用的也是復(fù)制構(gòu)造函數(shù),,詳情請見那個(gè)帖子《警惕,!C++里面“=”不一定就是等于(賦值)。 》
//-----------------------------------------------------------------------------------------------------------
//為什么,?
//-----------------------------------------------------------------------------------------------------------
三,、警惕!C++里面“=”不一定就是等于(賦值)
讓我們來現(xiàn)在看一個(gè)這樣的程序:
#include using namespace std; class HasPtr { public: int *ptr; int val; HasPtr(const int &p,int i):ptr(new int(p)),val(i) { } HasPtr& operator=(const HasPtr &rhs) { ptr = new int;
*ptr = *rhs.ptr; val =rhs.val; return *this; } ~HasPtr() { delete ptr; } };
int main() { int ival = 5; HasPtr a(ival,5); HasPtr b = a; cout<<*(a.ptr); return 0; }
這是看起來是一個(gè)沒有任何問題的程序,,并且在指針的回收處理上非常好,,用的是值型指針來處理類里面的指針,在VC(以后都是指VC++.net 2005)中編譯也可以通過,,在Dev-C++4.9.9.0 中編譯運(yùn)行都沒有問題,。但是在vc中運(yùn)行卻會(huì)出問題,。原因在哪里?經(jīng)我論壇發(fā)帖求教,,是因?yàn)镠asPtr b = a; 語句其實(shí)并不是賦值,,而是調(diào)用了構(gòu)造函數(shù)。不信,?證明如下:
#include <iostream> using namespace std; class HasPtr { public: int *ptr; int val; HasPtr(const int &p,int i):ptr(new int(p)),val(i) { } HasPtr(const HasPtr &orig):ptr(new int(*orig.ptr)),val(orig.val) { cout<<"Use me(copy constructor)"<<endl; } HasPtr& operator=(const HasPtr &rhs) { cout <<"Use me(=)"<<endl; *ptr = *rhs.ptr; val =rhs.val; return *this; } ~HasPtr() { delete ptr; } };
int main() { int ivala = 5; HasPtr a(ivala,5); HasPtr b = a; ivala = 6; cout<<*(a.ptr)<<*(b.ptr)<<endl; return 0; } 這一點(diǎn)在VC和在dev-c++中都是一樣的,。你會(huì)發(fā)現(xiàn)調(diào)用的都是copy constructor(復(fù)制構(gòu)造函數(shù)),不過據(jù)說之所以在dev-c++中沒有出錯(cuò),,是因?yàn)榭蓱z的dev-c++檢測能力太差,。。,。,。。,。,。。,。,。。 //------------------------------------------------------------------------------------------------------------------ //看完了,,也轉(zhuǎn)完了,,感謝作者。 //------------------------------------------------------------------------------------------------------------------ 四,,對于第三個(gè)問題在effective c++中,,第0章就有有拷貝構(gòu)造函數(shù)和等于操作符的區(qū)別: 例子: class Widget { Widget(); Widget(const& Widget rhs);//拷貝構(gòu)造函數(shù) Widget& operator= (const Widget& rhs);//assignment操作符 Widget w1; Widget w2(w1); w1 = w2;//調(diào)用=操作符 Widget w3 = w1;//調(diào)用拷貝構(gòu)造函數(shù) //區(qū)別:如果有新對象被定義,一定會(huì)有一個(gè)構(gòu)造函數(shù) } 是的,,就是這樣,!
|