天天看點

不要在構造函數和析構函數中調用虛函數,析構函數不能抛出異常1. 不要在構造函數中調用虛函數的原因2.不要在析構函數中調用虛函數的原因

https://blog.csdn.net/k346k346/article/details/49872023

雖然可以對虛函數進行實調用,但程式員編寫虛函數的本意應該是實作動态聯編。在構造函數中調用虛函數,函數的入口位址是在編譯時靜态确定的,并未實作虛調用。但是為什麼在構造函數中調用虛函數,實際上沒有發生動态聯編呢?

1. 不要在構造函數中調用虛函數的原因

第一個原因,在概念上,構造函數的工作是為對象進行初始化。在構造函數完成之前,被構造的對象被認為“未完全生成”。當建立某個派生類的對象時,如果在它的基類的構造函數中調用虛函數,那麼此時派生類的構造函數并未執行,所調用的函數可能操作還沒有被初始化的成員,浙江導緻災難的發生。

第二個原因,即使想在構造函數中實作動态聯編,在實作上也會遇到困難。這涉及到對象虛指針(vptr)的建立問題。在Visual C++中,包含虛函數的類對象的虛指針被安排在對象的起始位址處,并且虛函數表(vtable)的位址是由構造函數寫入虛指針的。是以,一個類的構造函數在執行時,并不能保證該函數所能通路到的虛指針就是目前被構造對象最後所擁有的虛指針,因為後面派生類的構造函數會對目前被構造對象的虛指針進行重寫,是以無法完成動态聯編。

2.不要在析構函數中調用虛函數的原因

同樣的,在析構函數中調用虛函數,函數的入口位址也是在編譯時靜态決定的。也就是說,實作的是實調用而非虛調用。 

考察如下例子。

#include <iostream>
using namespace std;

class A{
public:
    virtual void show(){
        cout<<"in A"<<endl;
    }
    virtual ~A(){show();}
};

class B:public A{
public:
    void show(){
        cout<<"in B"<<endl;
    }
};

int main(){
    A a;
    B b;
}
           
  • 是: 
  • in A 

    in A

在類B的對象b退出作用域時,會先調用類B的析構函數,然後調用類A的析構函數,在析構函數~A()中,調用了虛函數show()。從輸出結果來看,類A的析構函數對show()調用并沒有發生虛調用。

從概念上說,析構函數是用來銷毀一個對象的,在銷毀一個對象時,先調用該對象所屬類的析構函數,然後再調用其基類的析構函數,是以,在調用基類的析構函數時,派生類對象的“善後”工作已經完成了,這個時候再調用在派生類中定義的函數版本已經沒有意義了。

是以,一般情況下,應該避免在構造函數和析構函數中調用虛函數,如果一定要這樣做,程式猿必須清楚,這是對虛函數的調用其實是實調用。

析構函數不能抛出異常

(1) 如果析構函數抛出異常,則異常點之後的程式不會執行,如果析構函數在異常點之後執行了某些必要的動作比如釋放某些資源,則這些動作不會執行,會造成諸如資源洩漏的問題。

(2) 通常異常發生時,c++的機制會調用已經構造對象的析構函數來釋放資源,此時若析構函數本身也抛出異常,則前一個異常尚未處理,又有新的異常,會造成程式崩潰的問題。