對c++多態的探解
什麼是多態呢?請看如下程序
例1
class Base
{
public :
virtual void func()=0;
};
class Drive1 : public Base
{
public :
virtual void func()
{
cout<<"Drive1::func"<<endl;
}
};
class Drive2 : public Base
{
public :
virtual void func()
{
cout<<"Drive2::func"<<endl;
}
};
int main(int argc, char **argv)
{
int i;
Base *p[1];
p[0] = new Drive1();
p[1] = new Drive2();
for(i = 0; i < 2; i++)
{
p[i]->func();
}
return 0;
}
//output
Drive1::func
Drive2::func
這就是多態。 c++中的多態通常是用虛函數實現的,但也有中template技術實現的,ATL中就是用template的技術實現了多態。就性能上說,用虛函數的方法,程序的性能會相對較差點。那麼編譯器到底悄悄做了什麼呢?請看小馬哥翻譯的 “ATL布幔之下的祕密”http://www.vckbase.com/document/viewdoc/?id=1350。完了再往下看。我們知道當類中有虛函數的時候,編譯器會偷偷給類加上一個vfptr指針,指針指向一個vtable,vtable中虛函數的地址。不信?請看vs給的圖。
有類:
class Foo
{
public :
virtual void func() {}
public :
int a;
};
產生一個對象 Foo f
那麼定義一個Foo的指針會是什麼情況呢? 請看-> Foo *p
同樣,Foo類型的指針指向的內存塊也有vfptr和成員變量a,只是沒有”初始值“。所以可以猜想,當把派生類的指針賦給父類指針的時候,派生類的基類子對象裏的vfptr會賦值給基類指針的vfptr,從而實現了多態的行爲。
請看例1的程序,調試截圖。
那麼多重繼承的情況又是如何?考慮如下程序
#include <iostream>
using namespace std;
class Base1
{
public :
virtual void func()=0;
};
class Base2
{
public :
virtual void func()=0;
};
class Drive : public Base1, public Base2
{
public :
virtual void func()
{
cout<<"Drive::func"<<endl;
}
};
int main(int argc, char **argv)
{
Base1 *p1 = new Drive;
Base2 *p2 = new Drive;
p1->func();
p2->func();
return 0;
}
多重繼承情況下,派生類的對象裏會有連個vfptr,排放的順序和繼承的順序相關。
當把派生類的指針賦值給p2的時候,好心的編譯器又會偷偷的加上些東西。由於派生類的Base2子對象的vfptr排在Base1子對象的vfpt的後邊,所以就會有一個偏移量--4個字節,(因爲指針是一個32的整數,4*8)。當Base2 *p2 = new Drive;時,new返回的指針會加上4再賦值給p2,編譯器都爲我們做好了。
看圖
裏邊的[thunk]:Drive::func`adjustor{4}`就爲4個字節的偏移量,thunk結構由編譯器維護。具體結構挺複雜,有待研究。
現在,相信你對c++會有新的認識了吧。c++的編譯器太好心了,總是偷偷的,默默無聞的爲我們操心。