12/11/2009

Invoke pure virtual function

沒錯 你沒看錯
這篇的標題是呼叫純虛擬函式
一個沒有實作的純虛擬函式是有可能被呼叫起來的
class A {
public:
    virtual void f(void) = 0;
};
class B : public A {
    virtual void f(void) {}
};
在系統開發中 這是一個很常出現的程式片斷
在多型體系中
A* a = new B;
a->f();
因為vtable的存在 程式會為f找尋合適的function pointer並呼叫
這例子中從vtable中會找到class B中的f()
這並沒有甚麼問題
那麼如果vtable中沒有class B的資訊時會怎麼樣 那就會找到class A的f()
就會呼叫純虛擬函式了

這會發生嗎?
會的
class A;
class A {
public:
    virtual ~A() { m_pA->Close(); }
    virtual void Close(void) = 0;
    A* m_pA;
};
class B : public A {
    virtual void Close(void) {}
};
像這樣的程式
一般人會想說把所有的CloseXXX, DeleteXXX, DestoryXXX寫在解構子
解構子被呼叫的順序是從下而上
所以B的解構子先呼叫 把vtable清掉
A的解構子再呼叫 這時vtable中只有class A 所以就呼叫一個純虛擬函式了

當然 Effective C++裡提到 不應該在解構子呼叫虛擬函式 會有類似的問題出現
而在多執行緒中 也可能發生
class B : public A {
    virtual void f(void) {}
    void ThreadProc() {
        while(1)
            f();
    }
};

void main() {
    A* a = new B();

    while(1) {
        if (...)
            a->Create();
        if (exception == true)
            break;
    }
    delete a;
}
我們把class B改為一個thread 它會不斷的呼叫f()
當一個執行緒因為例外發生或其他原因 使物件的解構子被呼叫
呼叫到一半的時候 另一個執行緒呼叫該物件的虛擬函式
剛好解構到vtable只剩下Base class時 就會呼叫純虛擬函式了