对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++的编译器太好心了,总是偷偷的,默默无闻的为我们操心。