正確理解C 的建構函式和析構函式

2022-09-27 02:15:16 字數 3779 閱讀 4541

目錄

首先,由於類只是乙個模板,因此我們在定義類時無法對成員變數初始化,比如下面**就是錯誤的:

class circle;

因此,初始化只能發生在類建立物件的過程中,但是由於訪問許可權的原因,無法在類外訪問某些成員變數,因此下面這種做法有時候是無效的:

circle c1; // 例項化乙個物件 c1

c1.m_l = 20; // 通過建立的物件,來給對應變數初始化,但是如果m_l是private訪問許可權,則失效

為了解決這個問題,讓程式設計師能像使用標準資料型別一樣適用物件,在類內提供了乙個特殊的成員函式——「建構函式」,專門用於在建立物件時初始化類物件。之所以說它特殊,是因為c++已經自動為建構函式提供了名稱和使用語法,程式設計師只需要提供方法的定義即可,即:類名(形參列表)。具體來說,建構函式的定義如下:

class circle

};circle c1(20); //呼叫格式

cout << "c1.m_l:" << c1.m_l <

看上去似乎很簡單,但是由於建構函式也是函式,因此所有c++中的形參傳遞方式,函式特性以及函式呼叫方法都能用於建構函式。 如前文所講,c++會自動給類新增乙個空的建構函式,但是如果自己在類中實現了有參建構函式,編譯器便不再提供無參建構函式。舉例如下:

class circle

};circle c1(20); //呼叫格式正確,能夠通過建構函式賦值

circle c2; //錯誤,自己定義了有參建構函式,不再提供無參建構函式

建構函式可以過載,接著上面的例子,如果過載乙個空的建構函式,那麼兩個呼叫格式都正確:

class circle // 空建構函式

circle(const int a) //通過建構函式對成員變數進行賦值

};circle c1(20); //呼叫格式正確,能夠通過建構函式賦值

circle c2; //正確,可以通過過載的空建構函式實現初始化

建構函式的引數不僅可以是標準資料型別,也可以是類。眾所周知,在數值作為函式引數進行傳遞的時候,會重新拷貝出來乙份資料作為引數傳遞用完即銷毀,這種方式不僅浪費了記憶體空間,而且無法修改原始資料。為了結合這兩者之間的優點,於是經常採取引用作為函式的引數。雖然引用是指標的一種特殊情況,但是指標太過於靈活,並且引用在形式上引用與普通的變數地用法並沒有什麼區別,因此使用起來更加方便。

class circle // 空建構函式

circle(const int a) //通過建構函式對成員變數進行賦值

//引用作為函式引數傳遞,並用const修飾,節省空間的同時避免修改原資料

circle(const circle& sub_circle)

};circle c1(20); //呼叫格式正確,能夠通過建構函式賦值

circle c2(c1); //正確,可以通過拷貝建構函式進行初始化

c++中,乙個類包括:

雖然為了整合,我們將其寫到乙個類裡面,但是只有普通成員變數真正屬於類的物件,類的所有物件共享乙份靜態成員函式,靜態成員變數和普通成員函式。畫出了記憶體模型,如下圖所示:

為了進一步理解,我們舉例如下:

class person

};person p1(10, 20);

cout << "p1 所佔的空間為:" << sizeof(p1) << endl;

輸出結果為:

p1 所佔的空間為:1

這個題目在《劍指offer》一書中也提到過,由空類例項化出來的物件所佔的記憶體空間是1個而不是0個位元組,因為編譯其給物件p1分配了乙個位址,來表示不同的物件儲存在不同的位址空間,因此占用1個位元組。

class person

};person p1(10, 20);

cout << "p1 所佔的空間為:" << sizeof(p1) << endl;

輸出結果為:

p1 所佔的空間為:1

當向類中加入了成員函式與靜態成員變數時,類的例項化物件仍然只占用1個位元組的空間,足以證明這些函式和變數並不是類物件的一部分。

class person

};輸出結果為:

p 所佔的空間為:4

因此當向類中加入了非靜態成員變數時,類的例項化物件占用4個位元組的空間,可以說明,非靜態變數屬於類物件的一部分。綜上:同乙個類所有例項化出來的物件共享同乙份靜態成員變數,所以一改全改。既然同乙個類的不同物件共享同乙份成員函式,那麼成員函式怎麼區分該訪問哪個物件的普通程式設計客棧成員變數呢?

接著上一小節的問題,this指標為上述問題提供了乙個完美的解決方案,它指向用來呼叫成員函式的物件(被當作引數隱式地傳遞給成員函式),我們通過一張圖來理解它:

此外,this指標的另乙個用途是當成員函式需要返回物件時,用return *this; 或者return this,這種做法能夠實現鏈式程式設計。比如:

p2.addperson(p1).addperson(p1);

首先,物件p2呼叫成員函式addperson(p1),其返回值繼續呼叫addperson(p1),此時返回值就必須也是person型別才可以,因此使用this指標可以完成需求。先來看第乙個例子:

class person

// 返回值為person型別,且引數加上了const限制,防止修改原資料

person addperson(const person& p) };

person p1(20);

person p2(10);

person p3 = p2.addperson(p1).addperson(p1);

cout << "p1 age:" << p1.age << endl;

cout << "p2 age:" << p2.age << endl;

cout << "p3 age:" << p3.age << endl;

首先,通過建構函式分別對p1,p2賦了初值,然後p2呼叫函式addperson(p1)修改自身的變數age。**由於函式通過值傳遞的方式返回person型別,所以將整個person型別複製了乙份返回,返回值繼續呼叫addperson(p1), **最後的結果賦值給了新的物件p3。所以輸出結果為:

p1 age:20

p2 age:30

p3 age:50

但是如果函式addperson()修改為:

person& addperson(const person& p)

person p1(20);

person p2(10);

person p3 = p2.addperson(p1).addperson(p1);

cout << "p1 age:" << p1.age << endl;

cout << "p2 age:" << p2.age << endl;

cout << "p3 age:" << p3.age << endl;

與上例唯一的區別就在於返回值的型別變成了引用,那麼每次返回的就變成了該物件本身,而非在值傳遞中拷貝出來的那乙份資料。那麼輸出就變www.cppcns.com成了:

p1 agewww.cppcns.com:20

p2 age:50

p3 age:50

用建構函式建立物件後,程式負責跟蹤該物件,知道其過期為止。當物件過期時,程式自動呼叫析構函式完成清理工作。與建構函式一樣,c++預設提供了乙個空的析構函式,定義為:~類名( )。由於開闢在棧區的變數程式會自動釋放,因此不需要析構函式執行清理工作,但是當程式設計師在堆區開闢空間時,需要手動執行清理工作,這時候需要析構函式來釋放堆區記憶體。比如:

~person()

person p1(20);

person p2(10); // 在生命週期結束後自動呼叫析構函式執行清理工作

輸出為:

呼叫析構函式

呼叫析構函式

C 建構函式和析構函式

1.建構函式是類的一種特殊方法,每次建立類的例項都會呼叫它。在建立乙個類的例項時,建構函式就像乙個方法一樣被呼叫,但不返回值。語法格式 訪問修飾符 類名 特性 1 其名字必須與類名相同,例如 public class myclass 2 不能被直接呼叫,必須通過new運算子來 呼叫。publiccl...

C 建構函式和析構函式

建構函式 class rectangel rectangel int l,int w 這是帶引數的建構函式,建構函式都是沒有返回值,並且和該類同名 int area 這是另一種形式的內聯函式,把宣告和定義寫在一起的也是內聯函式 private int length int width 析構函式 cl...

c 建構函式和析構函式

1.c 的建構函式有預設建構函式,一般的建構函式,賦值建構函式,拷貝建構函式 複製建構函式 強制型別轉化建構函式。2.如果沒有定義建構函式和析構函式,則c 編譯器會按照 位拷貝 的方式提供預設的建構函式 不初始化 預設的賦值建構函式 淺賦值 預設的拷貝建構函式 淺拷貝 預設的析構函式。位拷貝要小心指...