Re: [問題] virtual function 的疑惑1

看板C_and_CPP作者 (我要加入劍道社!)時間14年前 (2010/02/10 15:48), 編輯推噓5(502)
留言7則, 6人參與, 最新討論串2/2 (看更多)
※ 引述《QQ29 (我愛阿蓉)》之銘言: : 若是這樣 : 照他寫法似乎 Base1::vptr會包含Derive的virtual function address : 所以他在程序15. 有辦法呼叫fd gd 透過Base1的指標.... : (所以是看繼承的順序 先繼承的先建構 也先把Derived 的virtual func填入vptr?) : 以上是我目前的觀念 請問有沒有什麼錯誤呢?? 應該對吧?嗯 我也不太有自信就是了 XD 另外對於 compiler 如何實作 virtual table 這件事 C++ 標準並沒有明確規範 所以上面只能說是一種最常見的實作方式 : 再來是要問一下我卡關的地方 : 進入程序16. : 他這三行 : cout << (DWORD)static_cast<Base1*>((Drive*)SOME_VALUE)-SOME_VALUE << endl; : cout << (DWORD)static_cast<Base2*>((Drive*)SOME_VALUE)-SOME_VALUE << endl; : cout << (DWORD)static_cast<Base3*>((Drive*)SOME_VALUE)-SOME_VALUE << endl; : 他說會印出 0 4 8 : 但我無法理解為什麼印出048 而他印這些用意何在.... : 以至於下面我也無法看下去 = = : 煩請觀念好的板友們給點意見 : 我覺得這網頁看懂了可能會有點幫助 : 謝謝 首先呢 你必需知道 class 中的 member function 到底是怎麼回事。 一般沒有 virtual 的 member function,它其實呢,和一般的 function 是一樣的, 不同的地方在於 compiler 會偷偷幫他加一個參數,也就是 this。 所以當你這樣寫的時候: class Foo { public: void func(int i) { cout << my_data + i << endl; } private: int my_data; }; Foo a; a.func(10); Compiler 會把它轉成如下的 code: struct Foo { int my_data; }; Foo_func(Foo* this, int i) { cout << this->my_data + i << endl; } Foo a; Foo_func(&a, 10); OK,那現在遇到多重繼承的時候呢? class Base1 { public: void func1(); private: int data1; }; class Base2 { public: void func2(); private: double data2; }; class Derived : public Base1, public Base2 { // ... }; Derived obj; obj.func1(); obj.func2(); Compiler 一樣把它翻成如下的形式: void Base1_func1(Base1* this); void Base2_func2(Base2* this); Derived obj; Base1_func1(&obj); Base2_func2(&obj); 但是這邊會遇到一個大問題:Derived 是 Base1 和 Base2 「黏」起來的物件: obj ───→ ┌──────┐ │Base1的成員 │ │ (data1) │ ├──────┤ │Base2的成員 │ │ (data2) │ └──────┘ 把 obj 這個物件的位址,直接當作一個 Base1 的物件去傳給 Base1_func1, 是沒有問題的,因為 Base1 的成員剛好就在 Derived 的前半部,然而若是 拿 obj 去傳給 Base2_func2,問題就大了,因為中間有個 offset。 所以 compiler 在這動了點手腳。因為 compiler 知道 Base1 的大小,所以 只要把 obj 的位址,加上 Base1 的大小,就會剛好對到 obj 中 Base2 的那部份。 因此紅色的那一行,會被 compiler 改成這樣: Base2_func2( (Base2*)((int)&obj + sizeof(Base1) ) ); obj+4 ─┐ ┌──────┐ │ │Base1的成員 │ │ │ (data1) │ └─→ ├──────┤ │Base2的成員 │ │ (data2) │ └──────┘ 這麼一來,傳進 Base2_func2 的 this 指標才會是正確的。 回到你的範例,因為 Base1 Base2 Base3 中都有 virtual table, 所以它們都會占用 4bytes 的空間 (在 32bits 的系統中) 這個範例就是告訴你, 當你把 Derived object 轉型成 Base object 時, this 指標要加上多少的 offset 才能傳進 Base 的 member function 中。 而這個 0 4 8 其實就對應到 Base1 和 Base2 在 Derived 中所占用的空間。 所以 Derived 不需要加上 offset 就可以轉型成 Base1 (因為 Base1 占用的是 Derived 中的第一段) Derived 需要加上 4bytes 才能轉型成 Base2 (因為 Base2 前面的 Base1 占了 4bytes...用來存 vtable) Derived 需要加上 8bytes 才能轉型成 Base3 (因為前面的 Base1 和 Base2 各占 4bytes...也是存 vtable) 以上... - 附註:我覺得處理這個 offset 造成的問題,可以說是多重繼承的一個缺點。 而且 virtual inheritance 還會讓情況變得更複雜 (會有某些物件被硬生生拆成兩段...XD) 如果你自認為頭腦夠清楚,可以看看這篇文章 (我一直還沒看完就是了 XD) Member Function Pointers and the Fastest Possible C++ Delegates http://www.codeproject.com/KB/cpp/FastDelegate.aspx -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 140.112.29.108

02/10 16:03, , 1F
push....:)
02/10 16:03, 1F

02/10 17:26, , 2F
讚啦
02/10 17:26, 2F

02/10 20:53, , 3F
每一家compiler實作layout的方式可能也會有所不同..
02/10 20:53, 3F

02/10 20:54, , 4F
這塊真的很複雜XD
02/10 20:54, 4F

02/10 23:23, , 5F
cool!
02/10 23:23, 5F

02/11 00:59, , 6F
好文推
02/11 00:59, 6F

08/20 09:25, , 7F
好文推~
08/20 09:25, 7F
文章代碼(AID): #1BScJFGz (C_and_CPP)
文章代碼(AID): #1BScJFGz (C_and_CPP)