虚函数原理
虚函数的一般实现模型:每个类有一个虚函数表,内含该类中有作用的虚函数地址。每个 对象有一个vptr(虚函数表指针)指向虚函数表
如下Person类
class Person
{
public:
virtual ~Person();
virtual string& getName();
virtual string& setName();
protected:
string name_;
};
在Person的对象Jack中,有两个东西,一个是数据成员name_,一个是_Vptr_Person(指向虚函数表的指针),其中_Vptr_Person存储着以下东西:
如果Man集成class Person,Man如下所示:
class Man:Person
{
public:
~Man();
string& getName();
protected:
string hasWhat_; //有什么呢 哈哈
};
在Man对象Star中有三个东西,一个是从Person中继承而来的name_,一个是自己的hasWhat,都是数据成员,另一个为_Vptr_Man(虚函数表指针):
其中,如果Man类里面重写了Person类里面的虚函数,则虚函数表被重写函数的对应位置替换为派生类里面的函数地址,如Man的虚函数表中,将Person::~Person() Person::getName();的地址替换为Man::~Man() 和Man::getName(),没有重写的则依旧为基类虚函数的地址
而且,此时,Man类可作为基类,继续被继承,满足如上关系。
若派生类继承多个具有虚函数的类,因为需要向上类型转换,所以派生类里面就有多个虚函数指针。则其结构体大小会增加一个虚函数指针的大小,如下代码所示:
class A
{
virtual void f()
{
}
};
class B
{
virtual void g();
};
class C:public B
{
};
class D:public B,public A
{
};
int main ()
{
cout<<sizeof(A)<<endl;//A中没有成员变量,只有一个虚函数表指针
cout<<sizeof(B)<<endl;//同A
cout<<sizeof(C)<<endl;//继承了B 有一个虚函数表指针
cout<<sizeof(D)<<endl;//继承了A 和 B,有两个虚函数表指针
return 0;
}
具体输出依赖于机器,但是满足 sizeof(A) = sizeof(B) = sizeof(C) = sizeof(D) / 2;
注意:虚函数指针在类中的位置,取决于编译器具体实现,不过一般虚函数表指针的位置在类的首部,即类最开始的位置。