Posted on March 3rd, 2010 at 0:19 by fr3@K
Background
不知道你有沒有過這樣的經驗 - 想要在父類的 constructor 裏面呼叫被子類 override (或是 implement) 的 (pure) virtual function?
-
struct Base
-
{
-
Base()
-
: x(0)
-
{
-
on_create();
-
}
-
virtual void on_create() {}
-
int x;
-
};
-
struct Derived : Base
-
{
-
Derived()
-
: y(1) {}
-
void on_create()
-
{
-
// do Derived specific things
-
}
-
int y;
-
};
-
-
void foo()
-
{
-
Derived d;
-
}
我試過. 跟我一樣這樣玩過的朋友, 很快就會學到它不會如你所期望的那樣運作. 如果你的期望是當建構 Derived 的 object instance 時, Derived::on_create 會被呼叫. 要是 Base::on_create 宣告成 pure virtual 的話, 你更會發現它甚至 不能 compile.
這樣的結果或許不是那麼直覺, 但背後的原因卻非常簡單 - virtual function 的 override 是發生在 object instance 被建構的時候.
-
=== mini tutorial ===
Class Derived 的 object layout 長得像下面這樣 (p 為指向一 Derived 的 object 的指標):
+---------+ +-----------------------------+
p----->| vptr |----->| ptr to typeinfo for Derived |
+---------+ +-----------------------------+
| x | | Derived::on_create |
+---------+ +-----------------------------+
| y |
+---------+
由於擁有 virtual function 的原因, Derived 的 object instance 在記憶體中會有一個指向其 vtable (virtual table) 的 vptr (virtual pointer).
=== end mini tutorial ===
Behind the Scenes
打開你的 Inside the C++ Object Model 複習一下. 當我們在程式中建構一個 Derived 的 object instance 時, 先被建構的是其父類 - 也就是 class Base 的部份 - 而後, 才會建構屬於 class Derived 的部份. 這過程可約略分為幾個階段:
- 準備
Base - 起始
Base的 member, 也就是Base::x - 呼叫
Base的 constructor Base部份建構完成- 準備
Derived - 起始
Derived的 member, 也就是Derived::y - 呼叫
Derived的 constructor Derived完整建構
許多人可能不曉得 object 的建構還有上述階段一與階段五的 "準備" 工作. 這裡說的準備, 說穿了其實就是設定 object 的 vptr! 也就是說一直到程式完成步驟五之前, 這個 object 的 vptr 指向的是屬於 class Base 的 vtable:
+---------+ +-----------------------------+
p----->| vptr |----->| ptr to typeinfo for Base |
+---------+ +-----------------------------+
| x | | Base::on_create |
+---------+ +-----------------------------+
| y |
+---------+
完成了步驟五, 這個 object 的 vptr 才會改而指向 class Derived 的 vtable. Derived::on_create 正是在這個時候 override 了 Base::on_create:
+---------+ +-----------------------------+
p----->| vptr |--+ | ptr to typeinfo for Base |
+---------+ | +-----------------------------+
| x | | | Base::on_create |
+---------+ | +-----------------------------+
| y | |
+---------+ | +-----------------------------+
+-->| ptr to typeinfo for Derived |
+-----------------------------+
| Derived::on_create |
+-----------------------------+
因此, 在例中 Base 的 constructor (也就是表中的步驟三) 參考到的 on_create 不會是被 Derived override 的版本.
With a Little Twist
前面提到如果 Base::on_create 被宣告成 pure virtual, 任何嘗試建構 Derived 的程式將不能成功編譯.
因為在這情況下, (假裝這樣的程式可以 compile 並運行) 在開始步驟三時 object 的 layout 會是像這樣:
+---------+ +-----------------------------+
p----->| vptr |----->| ptr to typeinfo for Base |
+---------+ +-----------------------------+
| x | | <pure virtual marker> |
+---------+ +-----------------------------+
| y |
+---------+
Compiler 會在 class Base 的 vtable 內, 原本紀錄著 Base::on_create 的欄位標上一個特殊的 marker 以表示該 virtual function 為 pure virtual. 既然 pure virtual function 是不能被使用者以有意義的方式參考, 間接呼叫了 pure virtual function 的 Derived 的 constructor (Derived ctor -> Base ctor -> pure virtual) 當然也是無效的.
What About Destructors
在 destructor 內呼叫 (pure) virtual function 也有完全一樣的問題. 只要把文中 object 建構的步驟反過來就可以理解其中的原因.
Good night.
![]() |
|
| Previous Post « [Update] boost::decay documentation issue « |
Next Post » Quiz on C++ Object Model » |








我忘了之前在哪本書看過的,反正結論說,就是不要在 constructor 使用 virtual function 就對了。
Comment by av — March 3, 2010 @ 1:37