建構函式與析構函式中不呼叫虛函式

2021-08-22 06:02:33 字數 1588 閱讀 6765

**:

本文參考《effective c++》第九條款

在c++中,提倡不能在建構函式和析構函式中呼叫虛函式。

這是為什麼呢?

首先,我們先回顧一下c++虛函式的作用。 虛函式的引入是c++執行時多型的體現,通過呼叫虛函式可以在執行程式時實現動態繫結,體現了物件導向程式設計多型的思想。

那為何提倡不能在建構函式與析構函式中不能呼叫虛函式。接下來我們通過**分析在建構函式或者虛構函式中呼叫虛函式是否可行。

假設我們有兩種商品a, b。 我們要記錄著兩種商品的銷售記錄,每當銷售一種商品時,我們就要記錄下來。

class item ;

item::item()

class itema : public item ;

class itemb : public item ;

我們執行如下**:

itemb b;
乙個derived class b 物件會被建立, 在呼叫建構函式itemb() 之前, 基類的建構函式會首先被呼叫,即:基類成分會在派生類特有成分被構造之前被構造。 而在基類的建構函式中又呼叫了虛函式salerecord(), 在此,你認為基類建構函式中所呼叫的虛函式salerecord的實現版本是哪乙個呢,是基類的實現版本還是派生類b的實現版本呢?

我們會很自然的認為, 現在構建的是itemb的物件, 建構函式中呼叫的當然是派生類b的實現版本。 其實我們可以通過執行程式確認基類建構函式中呼叫的虛函式實現版本是基類所有的,而不是派生類的實現版本。即使我們現在建立的是乙個派生類。

原因其實很簡單,在建立派生類的時候,基類先於派生類被構造,編譯器或者程式其實目光是很短淺的或者說是很現實的,在呼叫基類建構函式的時候,它並不知道你最終是要建立乙個基類還是派生類, 它只要把現在手頭上的工作做好——建立乙個基類。 對編譯器來說,此時所有可見的資訊包括data member 以及data function 都是基類所有的,它並不知道派生類的任何資訊, 而且它所做的行為也只是初始化派生類空間專屬於基類的那一部分, 不會越界。用一句話總結: 物件在呼叫什麼建構函式的時候,它就是什麼物件,他並不會像先知一樣能看的更遠。

以之前的**為例。 建立派生類的時候, 基類首先被建立, 此時它只是乙個基類,它的所見所為都完完全全跟建立乙個基類物件一樣,並不會下降到派生類。

我們可以極端一點,假設基類在建立的時候可以呼叫虛函式的派生類實現版本(其實不可能)。 此時,又會發生什麼呢。從意圖上講,我們要呼叫虛函式的不同版本,從根本上講,是要對不同的資料進行操作, 這樣函式才有意義。比如說基類的虛函式版本是對基類的資料進行操作, 派生類的虛函式版本是對派生類的虛函式進行操作。

然而,我們不要忽略一點,假設基類可以呼叫虛函式的派生類實現版本,那麼我們操控的資料則是派生類所有的資料, 此時,派生類的建構函式還沒有呼叫,派生類的資料成員也不會有相應的初始化,這時候對派生類的資料成員操作完全是無意義的,從程式崩潰到機器冒煙都有可能。

對於析構函式, 與建構函式是幾乎一樣的,不能呼叫虛函式。

從上面的分析可以得出兩點結論:

1. 建構函式或者析構函式呼叫虛函式並不會發揮虛函式動態繫結的特性,跟普通函式沒區別。

2. 即使建構函式或者析構函式如果能成功呼叫虛函式, 程式的執行結果也是不可控的

建構函式與析構函式中不呼叫虛函式

本文參考 effective c 第九條款 在c 中,提倡不能在建構函式和析構函式中呼叫虛函式。這是為什麼呢?首先,我們先回顧一下c 虛函式的作用。虛函式的引入是c 執行時多型的體現,通過呼叫虛函式可以在執行程式時實現動態繫結,體現了物件導向程式設計多型的思想。那為何提倡不能在建構函式與析構函式中不...

建構函式 析構函式 虛析構函式

說析構函式之前,先說下建構函式。建構函式用來完成對物件的一系列初始化操作,主要作用有 1.給建立的物件建立乙個識別符號 2.為物件資料成員開闢記憶體空間 3.完成物件資料成員的初始化 當並未顯示的定義建構函式時,會生成乙個預設的建構函式,預設建構函式不能完成物件資料成員的初始化,只能給物件建立一識別...

虛函式與建構函式 析構函式

虛函式是多型的基礎 ps多型是乙個介面多個實現 多型條件 1.繼承 2.虛函式 3.父類指標指向子類 include using namespace std class myclass virtual void go1 virtual void go2 class classx public myc...