C 踩坑實戰之構造和析構函式

2022-09-28 09:24:07 字數 3182 閱讀 8348

目錄

我是練習時長一年的 c++ 個人練習生,喜歡野指標、模板報錯和未定義行為(undefined beh**ior)。之前在寫設計模式的『工廠模式』時,一腳踩到了構造、繼承和 new 組合起來的坑,現在也有時間來整理一下了。

眾所周知:在建立物件時,防止有些成員沒有被初始化導致不必要的錯誤,在建立物件的時候自動呼叫建構函式(無宣告型別),完成成員的初始化。即:

class c // 隱式,預設建構函式

class c = class() // 顯示,預設建構函式

class c = class("name") // 顯示,非預設建構函式

class* c = new class // 隱式,預設建構函式

using namespace std;

class stone ;

double radius;

public:

stone() ;

stone(int w, double r) : weight, radius

void showinfo()

};int main ()

觀察以下的**,我們發現 stone s2;s2 = 3.3; 這樣將乙個 double 型別的資料賦值給類型別並沒有出錯,這是隱式型別轉換,從引數型別到類型別。

using namespace std;

class stone ;

double radius;

public:

stone() ;

// 都關閉

stone(double r) : radius

stone(int w) : weight

void showinfo()

};int main ()

這是因為:接受乙個引數的建構函式允許使用賦值語法來為物件賦值。s2=3.3 會建立 stock(double) 臨時物件,臨時物件初始化後程式設計客棧,逐成員賦值的方式複製到物件中,在幾個建構函式中加入了 cout << this 的語句,由物件的位址不同,可以判斷該賦值語句額外生成了臨時物件。

為了防止隱式轉換帶來的危險,可以使用關鍵字 explicit 關閉這一特性,這樣就得顯式完成引數型別到類型別的轉換:s = stock(1.3);不過,得保證沒有二義性。

using namespace std;

class stone ;

double radius;

public:

stone() ;

// 都關閉

explicit stone(double r) : radius

explicit stone(int w) : weight

void showinfo()

};int main ()

上述**中,如果 stone(int w) 沒有被關閉,那麼 s2=3.3 將呼叫這一建構函式。所以建構函式建議都加上 explicit 宣告。

派生類要注意的是:派生類被構造之前,通過呼叫乙個基類的建構函式,建立基類完成基類資料成員的初始化;也就是說,基類物件在程式進入派生類建構函式之前被建立。那麼,可以通過初始化列表傳遞給基類引數,不傳遞的話,呼叫基類的預設的建構函式,如下述程式中的:gem(){}:stone()。

using namespace std;

class stone ;

double radius;

public:

stone() ;

stone(int w, double r) : weight, radius {};

void showinfo()

int getweight()

auto getradius() -> double

};class gem : public stone ;

gem(double p, int w, double r) : stone(w, r), price {};

void show()

};int main ()

物件過期時,程式會呼叫物件的析構函式完成一些清理工作,如釋放變數開闢的空間等。如建構函式使用了 new 來申請空間,析構就需要 delete 來釋放空間。如果沒有特別宣告析構函式,編譯器會為類提供預設的析構函式,在物件作用域到期、被刪除時自動被呼叫。

如 stock1 = stock(),這種就申請了乙個臨時變數,變數消失時會呼叫析構函式。此外,這種區域性變數放在棧區,先入後出,也就是,最後被申請的變數最先被釋放。

using namespace std;

class stone ;

double radius;

public:

stone() ;

~stone()

};int main ()

return 0;

}繼承模擬較容易理解,畢竟都學過物件導向。公有繼承的時候,基類的公有成員也是派生類的共有成員;私有成員也是派生類的一部分,不過需要共有或保護方法來訪問。但是但是但是,派生類和基類的析構函式之間,也是乙個坑。在繼承中:

在繼承中,物件的銷毀順序和建立相反。建立時先建立基類,而後建立子類;銷毀時,先呼叫子類的析構函式,而後自動呼叫基類的析構函式。因此,對於基類而言,建議將析構函式寫成虛方法。如果析構不是虛方法,對於以下情況,只有基類的析構被呼叫;如果析構是虛方法,子類、基類的析構方法都被呼叫。可以嘗試刪除下述**的 virtual 來觀察結果:

using namespace std;

class stone ;

double radius;

public:

stone() ;

stone(int w, double r) : weight, radius {};

void showinfo()

int getweight()

auto getradius() -> double

virtual ~stone()

};class gem : public stone ;

gem(double p, int w, double r) : stone(w, r), price {};

void show()

~gem()

};int main ()

大概常見的坑在上面都記錄好了,來看一段我寫的危險的程式(我大概抽象了一下),覆蓋了:野指標和為定義行為:

using namespace std;

class a

~a()

};int main ()

C 之建構函式和析構函式

我們已經知道了物件就是類的例項,二者的關係就相當於資料型別與它的變數的關係。每個物件區別於其他物件的地方就是依靠它的自身屬性,即資料成員的值。c 中,物件在定義的時候進行的資料成員的設定,稱為物件的初始化。同樣,在特定物件使用結束時,還要對資料成員進行一些清理工作。c 中對類的初始化和清理的工作,分...

C 之建構函式和析構函式

一 建構函式 1 物件的初始化和建構函式 類是一種抽象的資料型別,它不占用儲存空間,不能容納具體的資料。因此在類宣告中不能給資料成員賦初值。例如 錯誤 class complex 與使用變數一樣,使用物件時也應該先定義,後使用。在定義物件時,對資料成員賦初值,稱為初始化。例如 class compl...

C 之建構函式和析構函式強化

構造與析構呼叫順序 include class test9 1 析構的順序和構造的順序相反,先構造的後析構 test9 1 private int m a 物件初始化列表,解決乙個類中有另乙個沒有無參構造的類的物件的初始化 class test9 2 test9 2 private test9 1 ...