拷貝構(gòu)造函數(shù)
??如果一個(gè)構(gòu)造函數(shù)的第一個(gè)參數(shù)是自身類類型的引用荔燎,且任何額外參數(shù)都有默認(rèn)值,則此構(gòu)造函數(shù)是拷貝構(gòu)造函數(shù)艰山。
class Foo {
public:
Foo(); //默認(rèn)構(gòu)造函數(shù)
Foo(const Foo&); //拷貝構(gòu)造函數(shù)
//...
};
??拷貝構(gòu)造函數(shù)的第一個(gè)參數(shù)必須是引用類型湖雹,雖然我們可以定義一個(gè)接受非const引用的拷貝構(gòu)造函數(shù),但此參數(shù)幾乎總是一個(gè)const的引用曙搬∷だ簦拷貝構(gòu)造函數(shù)在幾種情況下都會(huì)被隱式地使用。因此纵装,拷貝構(gòu)造函數(shù)通常不應(yīng)該是explicit的征讲。
??合成拷貝構(gòu)造函數(shù)
??對(duì)某些類來說,合成拷貝構(gòu)造函數(shù)用來阻止我們拷貝該類類型的對(duì)象橡娄,而一般情況诗箍,合成的拷貝構(gòu)造函數(shù)會(huì)將其參數(shù)的成員逐個(gè)拷貝到正在創(chuàng)建的對(duì)象中。編譯器從給定對(duì)象中依次將每個(gè)非static成員拷貝到正在創(chuàng)建的對(duì)象中挽唉。
??拷貝初始化
??直接初始化與拷貝初始化的差異:
string dots(10,'.'); //直接初始化
string s(dots); //直接初始化
string s2=dots; //拷貝初始化
sttring null_book="9-999-99999-9"; //拷貝初始化
string nines=string(100,'9'); //拷貝初始化
??當(dāng)使用直接初始化時(shí)滤祖,我們實(shí)際上是要求編譯器使用普通的函數(shù)匹配來選擇與我們提供的參數(shù)最匹配的構(gòu)造函數(shù),當(dāng)我們使用拷貝初始化時(shí)瓶籽,我們要求編譯器將右側(cè)運(yùn)算對(duì)象拷貝到正在創(chuàng)建的對(duì)象中匠童,如果需要的話還要進(jìn)行類型轉(zhuǎn)換∷芩常拷貝初始化通常使用拷貝構(gòu)造函數(shù)來完成汤求。
??除了使用“=”定義變量時(shí)會(huì)發(fā)生拷貝初始化俏险,還有以下情況:
??1、將一個(gè)對(duì)象作為實(shí)參傳遞給一個(gè)非引用類型的形參
??2扬绪、從一個(gè)返回類型為非引用類型的函數(shù)返回一個(gè)對(duì)象
??3竖独、用花括號(hào)列表初始化一個(gè)數(shù)組中的元素或一個(gè)聚合類的成員
??拷貝初始化的限制
??如果我們使用的初始化值要求通過一個(gè)explicit構(gòu)造函數(shù)來進(jìn)行類型轉(zhuǎn)換,那么使用拷貝初始化還是直接初始化就不是無關(guān)緊要的了:
vector<int> v1(10); //正確:直接初始化
vector<int> v2=10; //錯(cuò)誤:接受大小參數(shù)的構(gòu)造函數(shù)是explicit的
void f(vector<int>); //f的參數(shù)進(jìn)行拷貝初始化
f(10); //錯(cuò)誤:不能用一個(gè)explicit的構(gòu)造函數(shù)拷貝一個(gè)實(shí)參
f(vecotr<int> (10)); //正確:從一個(gè)int直接構(gòu)造一個(gè)臨時(shí)vector
??編譯器可以繞過拷貝構(gòu)造函數(shù)
??在拷貝初始化過程中挤牛,編譯器可以跳過拷貝/移動(dòng)構(gòu)造函數(shù)莹痢,直接創(chuàng)建對(duì)象:
string null_book="9-999-99999-9" //拷貝初始化
改寫為:
string null_book=("9-999-99999-9"); //編譯器略過了拷貝構(gòu)造函數(shù)
拷貝賦值運(yùn)算符
??與類控制其對(duì)象如何初始化一樣,類也可以控制其對(duì)象如何賦值:
Sales_data trans,accum;
trans=accum; //使用Sales_data的拷貝賦值運(yùn)算符
??重載賦值運(yùn)算符
??重載運(yùn)算符的本質(zhì)上是函數(shù)赊颠,其名字由operator關(guān)鍵字后接表示要定義的運(yùn)算符的符號(hào)組成格二。因此,賦值運(yùn)算符就是一個(gè)名為operator=的函數(shù)竣蹦。例如:
class Foo {
public:
Foo& operator=(const Foo&); //賦值運(yùn)算符
//...
};
??合成拷貝賦值運(yùn)算符
??與處理拷貝構(gòu)造函數(shù)一樣顶猜,如果一個(gè)類未定義自己的拷貝賦值運(yùn)算符,編譯器會(huì)為它生成一個(gè)合成拷貝賦值運(yùn)算符痘括。合成拷貝運(yùn)算符返回一個(gè)指向其左側(cè)運(yùn)算對(duì)象的引用长窄。例如:
//等價(jià)于合成拷貝賦值運(yùn)算符
Sales_data&
Sales_data::operator=(const Sales_data &rhs)
{
bookNo=rhs.bookNo; //調(diào)用string::operator=
units_sold=rhs.units_sold; //使用內(nèi)置的int賦值
revenue=rhs.revenue; //使用內(nèi)置的double賦值
return *this; //返回一個(gè)此對(duì)象的引用
}
析構(gòu)函數(shù)
??析構(gòu)函數(shù)執(zhí)行與構(gòu)造函數(shù)相反的操作:構(gòu)造函數(shù)初始化對(duì)象的非static成員,還可能做一些其他工作纲菌;析構(gòu)函數(shù)釋放對(duì)象使用的資源挠日,并銷毀對(duì)象的非static數(shù)據(jù)成員。
class Foo {
public:
~Foo(); //析構(gòu)函數(shù)
//...
};
??由于析構(gòu)函數(shù)不接受參數(shù)翰舌,因此它不能被重載嚣潜。
??析構(gòu)函數(shù)完成什么工作
??在一個(gè)析構(gòu)函數(shù)中,首先執(zhí)行函數(shù)體椅贱,然后銷毀成員懂算,成員按初始化順序的逆序銷毀,在對(duì)象最后一次使用之后庇麦,析構(gòu)函數(shù)的函數(shù)體可執(zhí)行類設(shè)計(jì)者希望執(zhí)行的任何收尾工作计技。在一個(gè)析構(gòu)函數(shù)中,不存在類似構(gòu)造函數(shù)中初始化列表的東西來控制成員如何銷毀山橄,析構(gòu)部分是隱式的垮媒。
??什么時(shí)候會(huì)調(diào)用析構(gòu)函數(shù)
??無論何時(shí)一個(gè)對(duì)象被銷毀,就會(huì)自動(dòng)調(diào)用其析構(gòu)函數(shù)航棱,由于析構(gòu)函數(shù)自動(dòng)運(yùn)行睡雇,我們的程序可以按需要分配資源,而無須擔(dān)心何時(shí)釋放這些資源饮醇。
??合成析構(gòu)函數(shù)
??當(dāng)一個(gè)類未定義自己的析構(gòu)函數(shù)時(shí)入桂,編譯器會(huì)為它定義一個(gè)合成析構(gòu)函數(shù)。對(duì)于某些類驳阎,合成析構(gòu)函數(shù)被用來阻止該類型的對(duì)象被銷毀,如果不是這種情況,合成析構(gòu)函數(shù)的函數(shù)體就為空呵晚。
??在(空)析構(gòu)函數(shù)執(zhí)行完畢后蜘腌,成員會(huì)被自動(dòng)銷毀,析構(gòu)函數(shù)自身并不直接銷毀成員饵隙,成員是在析構(gòu)函數(shù)體之后隱含的析構(gòu)階段被銷毀的撮珠,在整個(gè)對(duì)象銷毀過程中,析構(gòu)函數(shù)體是作為成員銷毀步驟之外的另一部分而進(jìn)行的金矛。