這裡先執行個示例**:
#include
using
namespace
std;
class base
virtual
void fun()
virtual
void fun_() = 0;
~base()
};class derived :public base
virtual
void fun()
virtual
void fun_()
~derived()
};int main()
輸出結果:
base::base() //基類構造
base::fun()
derived::derived() //子類構造
derived::fun()
derived::~derived() //子類析構
derived::fun()
base::~base() //基類析構
base::fun()
在建立乙個物件時,會依次呼叫父建構函式->子建構函式,同樣,在析構時也會逆過來呼叫子析構函式->父析構函式。那麼在呼叫的時候,如果析構函式中對於虛函式還執行虛機制,就有可能已經執行過乙個子物件的析構函式,又去呼叫子物件的函式,這樣會很危險。所以在虛析構函式中,對於虛函式,只會執行目前最外一級的那個函式。
雖然可以對虛函式進行實呼叫,但程式設計師編寫虛函式的本意應該是實現動態聯編。在建構函式中呼叫虛函式,函式的入口位址是在編譯時靜態確定的,並未實現虛呼叫。但是為什麼在建構函式中呼叫虛函式,實際上沒有發生動態聯編呢?
1. 不要在建構函式中呼叫虛函式的原因
第乙個原因,在概念上,建構函式的工作是為物件進行初始化。在建構函式完成之前,被構造的物件被認為「未完全生成」。當建立某個派生類的物件時,如果在它的基類的建構函式中呼叫虛函式,那麼此時派生類的建構函式並未執行,所呼叫的函式可能操作還沒有被初始化的成員,浙江導致災難的發生。
第二個原因,即使想在建構函式中實現動態聯編,在實現上也會遇到困難。這涉及到物件虛指標(vptr)的建立問題。在visual c++中,包含虛函式的類物件的虛指標被安排在物件的起始位址處,並且虛函式表(vtable)的位址是由建構函式寫入虛指標的。所以,乙個類的建構函式在執行時,並不能保證該函式所能訪問到的虛指標就是當前被構造物件最後所擁有的虛指標,因為後面派生類的建構函式會對當前被構造物件的虛指標進行重寫,因此無法完成動態聯編。
2.不要在析構函式中呼叫虛函式的原因
同樣的,在析構函式中呼叫虛函式,函式的入口位址也是在編譯時靜態決定的。也就是說,實現的是實呼叫而非虛呼叫。
考察如下例子。
#include
using
namespace
std;
class a
virtual ~a()
};class b :public a
};int main()
程式輸出結果是:
in a
in a
在類b的物件b退出作用域時,會先呼叫類b的析構函式,然後呼叫類a的析構函式,在析構函式~a()中,呼叫了虛函式show()。從輸出結果來看,類a的析構函式對show()呼叫並沒有發生虛呼叫。
從概念上說,析構函式是用來銷毀乙個物件的,在銷毀乙個物件時,先呼叫該物件所屬類的析構函式,然後再呼叫其基類的析構函式,所以,在呼叫基類的析構函式時,派生類物件的「善後」工作已經完成了,這個時候再呼叫在派生類中定義的函式版本已經沒有意義了。
因此,一般情況下,應該避免在建構函式和析構函式中呼叫虛函式,如果一定要這樣做,程式猿必須清楚,這是對虛函式的呼叫其實是實呼叫。
不要在建構函式和析構函式中呼叫虛函式
提到建構函式和析構函式,想必大家肯定是非常了解,但是能否在建構函式或是析構函式中呼叫虛函式呢?答案是千萬不要這麼做,這麼做不會得到大家想要的結果。首先提一下建構函式,建構函式的順序是從基類開始構造 子類,如果在基類中呼叫虛函式,由於建構函式基類中僅存在自身 或其父類,如果存在 不會根據虛函式表的規則...
C 中建構函式和析構函式
定義 它是一種特殊的方法。主要用來在建立物件時初始化物件,即為物件成員變數賦初始值,總與new運算子一起使用在建立物件的語句中。另外,乙個類可以有多個建構函式 我們可以根據其引數個數的不同或引數型別的不同來區分它們 這就是建構函式的過載 特點 1.建構函式的命名必須和類名完全相同 2.建構函式的功能...
C 建構函式和析構函式
1.建構函式是類的一種特殊方法,每次建立類的例項都會呼叫它。在建立乙個類的例項時,建構函式就像乙個方法一樣被呼叫,但不返回值。語法格式 訪問修飾符 類名 特性 1 其名字必須與類名相同,例如 public class myclass 2 不能被直接呼叫,必須通過new運算子來 呼叫。publiccl...