構(gòu)造函數(shù)和析構(gòu)函數(shù)
一.構(gòu)造函數(shù)
構(gòu)造函數(shù)時一種特殊的函數(shù),它主要用于為對象分配空間,進行初始化。
(注:構(gòu)造函數(shù)沒有this指針)
1. 構(gòu)造函數(shù)的幾個特點:
函數(shù)名與類名相同
參數(shù)任意,但是沒有返回值,viod也不行
它是在實例化對象的時候自動的調(diào)用,而不需要用戶調(diào)用
構(gòu)造函數(shù)可以重載
構(gòu)造函數(shù)可以寫在類體內(nèi),也可以寫在類體外
2. 構(gòu)造函數(shù)的調(diào)用形式
(1) 類名對象名[(實參表)]
(2) 類名 *指針變量名 = new 類名[(實參表)]
下面是構(gòu)造函數(shù)的使用方法
#include<iostream>usingnamespace std; classDate{PRivate: int year; int month; int day;public: Date(int y,int m,int d); voidsetDate(int y,int m,int d); voidshowDate();}; Date::Date(inty, intm, intd){ cout << "Constcuting..." << endl; year = y; month = m; day = d;} voidDate::setDate(inty, intm, intd){ year = y; month = m; day = d;} inlinevoidDate::showDate(){ cout <<year << "." << month << "."<< day << endl;} int main(){ Datedate1(2017,3,2); cout << "date1:" << endl; date1.showDate(); date1.setDate(2017,3,3); cout << "date2:" << endl; date1.showDate(); return 0;}上面代碼中主函數(shù)中的構(gòu)造函數(shù)的使用是采用第一種方式,這里再提供第二種方式int main(){ Date *pdate; pdate = newDate(2017,2,3); cout << "Date1" << endl; pdate->showDate(); pdate->setDate(2017,2,3); cout << "Date2" << endl; pdate->showDate(); return 0; }這段代碼中編譯器開辟了一個存儲空間,并且存放了一個Date類,但是這個對象沒有名字,成為無名對象,但是該對象有地址,這個地址存放在pdate中,我們通過指針可以找到他,訪問時用new動態(tài)建立的對象一般是不用對象名的,而是通過指針進行訪問。如果不需要時,可以通過delete進行釋放。
二. 成員初始化列表
C++還提供了另一種初始化成員的方法——用成員初始化列表來實現(xiàn)對數(shù)據(jù)成員的初始化,這種方法不在函數(shù)體內(nèi)用賦值語句,而是在函數(shù)首部實現(xiàn)的。
例如在構(gòu)造函數(shù)的定義中可以使用如下的方式:
Date::Date(inty, intm, intd) :year(y),month(m), day(d){ cout << "Constcuting..." << endl;}帶有成員列表的構(gòu)造函數(shù)一般的形式如下:
類名 ::構(gòu)造函數(shù)名([參數(shù)表]):[(成員初始化列表)]
{}
成員初始化列表的一般形式為:
數(shù)據(jù)成員名1(初始值1),數(shù)據(jù)成員名2(初始值2),…
成員初始化列表有什么用途呢,一般對于const修飾的數(shù)據(jù)成員,或者是引用類型的數(shù)據(jù)成員。
注意:使用成員初始化列表初始化的時候,它的初始化的順序是按照在類中聲明的順序進行初始化的,而不是按照成員初始化列表中的順序進行初始化。
三. 帶默認(rèn)參數(shù)的構(gòu)造函數(shù)(帶缺省參數(shù))
在構(gòu)造函數(shù)中,有一些成員值是不變的,這時我們可以使用帶默認(rèn)參數(shù)的構(gòu)造函數(shù)
意思就是我們在定義構(gòu)造函數(shù)時,可以在形參的部分隊參數(shù)進行賦初值。
四. 析構(gòu)函數(shù)
析構(gòu)函數(shù)也是一種特殊的成員函數(shù),它通常用于撤銷對象時的一些清理任務(wù)
析構(gòu)函數(shù)的特點如下:
析構(gòu)函數(shù)和構(gòu)造函數(shù)名字相同,但是它的前面必須加一個波浪號(~)
析構(gòu)函數(shù)沒有參數(shù),也沒有返回值,而且不能重載。
析構(gòu)函數(shù)自動被調(diào)用
例子:
同樣是上文的Date類,我們可以在定義類的時候定義析構(gòu)函數(shù),如
class Date
{
…
~Date();
}
Date::~Date()
{
cout<<”destruting …”<<endl;
}
在以下情況下,當(dāng)對象的聲明周期結(jié)束時,析構(gòu)函數(shù)會被自動調(diào)用
(1). 如果定義了一個全局對象,則在程序流離開作用域(如main()函數(shù)結(jié)束或者調(diào)用exit()函數(shù))時,調(diào)用該全局對象的析構(gòu)函數(shù)。
(2). 如果一個對象被定義在一個函數(shù)體內(nèi),則當(dāng)這個函數(shù)調(diào)用結(jié)束時,該函數(shù)應(yīng)該釋放,析構(gòu)函數(shù)自動被調(diào)用
(3). 若一個對象是使用new運算符進行動態(tài)創(chuàng)建的,在使用delete運算符釋放它時,delete會調(diào)用析構(gòu)函數(shù)。
五. 默認(rèn)的構(gòu)造函數(shù)和默認(rèn)的析構(gòu)函數(shù)
1.默認(rèn)的構(gòu)造函數(shù)(系統(tǒng)自帶的構(gòu)造函數(shù),全參數(shù)構(gòu)造函數(shù),無參的構(gòu)造函數(shù)都可以叫默認(rèn)的構(gòu)造函數(shù))
一般寫程序時會定義構(gòu)造函數(shù),但是如果沒有定義構(gòu)造函數(shù),系統(tǒng)會自動生成一個構(gòu)造函數(shù),這就是默認(rèn)的構(gòu)造函數(shù)。上面的程序中,如果沒有定義構(gòu)造函數(shù),而直接使用Date date1;這時系統(tǒng)會為Date類生成下面形式的構(gòu)造函數(shù):
Date ::Date()
{}
并且使用這個默認(rèn)的構(gòu)造函數(shù)對date1進行初始化,但是這個構(gòu)造函數(shù)沒有任何參數(shù),它只能開辟一個存儲空間,而不能給對象中的數(shù)據(jù)成員賦值,這時的初始值是個隨機數(shù),程序運行的時候可能會造成錯誤。
補充說明:對沒有定義構(gòu)造函數(shù)的類,其公有數(shù)據(jù)成員可以用初始值列表進行初始化。
只要一個類定義了一個構(gòu)造函數(shù),系統(tǒng)將不再給它提供默認(rèn)的構(gòu)造函數(shù)
3. 默認(rèn)的析構(gòu)函數(shù)
每個類都有一個析構(gòu)函數(shù),如果一個類沒有定義析構(gòu)函數(shù),那么編譯系統(tǒng)會自動的生成一個析構(gòu)函數(shù)。
注意:在C++調(diào)用構(gòu)造函數(shù)的時候注意不能出現(xiàn)這種形式例如有一個類Date,這個時候?qū)嵗粋€對象Date date();這里不是調(diào)用了這個構(gòu)造函數(shù),而是一個函數(shù)的聲明,是錯誤的,如果不給這個對象傳遞參數(shù)就不要寫后面的括號,傳遞參數(shù)的時候?qū)懤ㄌ?,后面在加入?yún)?shù)。
六.拷貝構(gòu)造函數(shù)
拷貝構(gòu)造函數(shù)的形參是本類對象的引用,拷貝構(gòu)造函數(shù)的作用是在建立一個新得對象時,使用一個已經(jīng)存在的對象去初始化這個新對象。形如:Point p2(p1);
拷貝構(gòu)造函數(shù)的幾個特點:
1. 因為拷貝構(gòu)造函數(shù)也是構(gòu)造函數(shù),所以它的函數(shù)名必須與類名相同,而且也沒有返回值
2. 拷貝構(gòu)造函數(shù)只有一個參數(shù),而且是同類對象的引用
3. 每個類都有一個拷貝構(gòu)造函數(shù),可以自己定義用于初始化新的對象,如果沒有定義系統(tǒng)會自動的定義,用于復(fù)制與數(shù)據(jù)成員值相同的對象。
拷貝構(gòu)造函數(shù)的使用:
1. 自定義拷貝構(gòu)造函數(shù)
自定義拷貝構(gòu)造函數(shù)的一般形式如下:
類名::類名(const 類名 &對象名)
自定義拷貝構(gòu)造函數(shù)的調(diào)用:
代入法:類名 對象2(對象1);
賦值發(fā):類名 對象2 = 對象1;
2. 默認(rèn)的拷貝構(gòu)造函數(shù)
如果用戶沒有定義自定義的拷貝構(gòu)造函數(shù),然后又使用了拷貝構(gòu)造函數(shù),系統(tǒng)會調(diào)用默認(rèn)的拷貝構(gòu)造函數(shù),例如Rectangle p2(p1);此時會把p1中各個域的值均復(fù)制給p2
3. 調(diào)用拷貝構(gòu)造函數(shù)的三種情況
(1) 當(dāng)用類的一個對象去初始化類的另一個對象的時候
(2) 當(dāng)函數(shù)的形參是類的對象,調(diào)用函數(shù)進行形參和實參結(jié)合時
(3) 當(dāng)函數(shù)的返回值是對象,函數(shù)執(zhí)行完成返回調(diào)用者時
下面是具體的程序,還有注釋部分,注釋部分就是上面各種的講解
#include<iostream>usingnamespace std; classRectangle{private: int_length; int _width;public: Rectangle(int len =10, int wid =10); //構(gòu)造函數(shù) Rectangle(constRectangle&p); //拷貝構(gòu)造函數(shù) void disp();}; Rectangle::Rectangle(intlen, intwid) //構(gòu)造函數(shù){ _length = len; _width = wid; cout << "usingnormal constructor" << endl;} Rectangle::Rectangle(constRectangle &p) //拷貝構(gòu)造函數(shù){ _length = 2 * p._length; _width = 2 * p._width; cout << "usingcopy constructor" << endl;} voidRectangle::disp(){ cout <<_length << " " << _width << endl;} void fun1(Rectanglep){ p.disp();} Rectangle fun2(){ Rectanglep4(10,30); //這里調(diào)用了普通的構(gòu)造函數(shù) returnp4; //這里返回的一個Rectangle的對象,我們會使用p2 = fun2();去接受他的返回值,所以這里相當(dāng)于給p2用了一個拷貝構(gòu)造函數(shù)} int main(){ Rectangle p1(30,40); //定義了p1,調(diào)用構(gòu)造函數(shù) p1.disp(); Rectanglep2(p1); //調(diào)用拷貝構(gòu)造函數(shù)把p1里面的值全部復(fù)制給p2(情況1) p2.disp(); Rectangle p3 =p1; //調(diào)用拷貝構(gòu)造函數(shù),把p1的值復(fù)制給p3(情況1) p3.disp(); fun1(p1); //這個時候傳入的是p1的一份引用,然后會調(diào)用Rectangle的拷貝構(gòu)造函數(shù) //這里應(yīng)該思考的一個問題就是,為什么調(diào)用的是Rectangle的拷貝構(gòu)造函數(shù),這里我的一個猜測是,直接傳一個對象的時候,就像傳數(shù)組名一樣了,發(fā)生了一個轉(zhuǎn)換之類的 //可能就把對象轉(zhuǎn)換成對象的一個引用了吧 p1.disp(); p2 =fun2(); //函數(shù)的返回值是對象,屬于第三種情況調(diào)用拷貝構(gòu)造函數(shù) p2.disp(); system("pause"); return 0;}
六. 淺拷貝和深拷貝
所謂淺拷貝,就是如果沒有自定義拷貝構(gòu)造函數(shù)而直接使用的話,就只是單純的進行值得賦值,但是當(dāng)類的數(shù)據(jù)成員有指針類型的時候,并且在使用指針的時候,給這個指針動態(tài)的開辟了一個新的循存儲空間的話,當(dāng)我實例化一個對象a之后,a里面的一個數(shù)據(jù)成員p動態(tài)的開辟了一個存儲空間,然后我有實例化b,而且是通過調(diào)用未定義的拷貝構(gòu)造函數(shù),這樣就直接把a里面的內(nèi)容給了b,b里面的一個指針也指向剛剛a里面的內(nèi)存空間,這時候就出現(xiàn)了一種情況就是當(dāng)我調(diào)用析構(gòu)函數(shù)清理a 的內(nèi)存空間的時候,因為b中的指針也指向了那片空間,所以此時b中的那個指針就沒有意義了,這就造成了錯誤。所以建議再使用指針的相關(guān)操作的時候,盡量去自定義拷貝構(gòu)造函數(shù)。
新聞熱點
疑難解答
圖片精選