C++里面实现多态需要两个条件:1、基类里面的函数为虚函数,并且在派生类里面对基类里面的虚函数进行重写
2、通过基类的指针或者引用来进行调用
虚函数:因为在使用虚函数的时候会在对象的里面添加一张虚函数表,用来存储类里面的虚函数的地址。
但是在这里,虚表的最后面不一定是0x00000000,这个和使用的编译器有关系。
如果在派生类里面对基类里面的虚函数进行重写的话,在基类里面就会对基类的虚表相同偏移量的函数进行替换,这样在通过基类的指针或者引用进行调用的时候就会调用派生类里面的函数,而不是基类里面的函数
这样在调用函数的时候就会调用重写之后的函数,依照这种机制就可以实现简单的多态。
带虚拟继承的多态:
虚拟继承是依靠偏移量表来实现的,在多态的实现里面,稍微复杂一点。
举个栗子:
class B
{
public:
int _b;
virtual void Test()
{
cout<<"B::Test()"<<endl;
}
virtual void Test1()
{
cout<<"B::Test1()"<<endl;
}
};
class C:virtual public B
{
public:
int _c1;
virtual void Test()
{
cout<<"C::Test()"<<endl;
}
virtual void Test2()
{
cout<<"C::Test2()"<<endl;
}
};
代码存储模型如下:
在重写虚函数之后在调用的时候就会嗲用派生类的虚函数,而且在菱形继承里面也不会出现二义性的问题。
菱形虚拟继承的剖析:
代码如下:
class B
{
public:
int _b;
virtual void Test()
{
cout<<"B::Test()"<<endl;
}
virtual void Test1()
{
cout<<"B::Test1()"<<endl;
}
};
class C1:virtual public B
{
public:
int _c1;
virtual void Test()
{
cout<<"C1::Test()"<<endl;
}
virtual void Test2()
{
cout<<"C1::Test2()"<<endl;
}
};
class C2:virtual public B
{
public:
int _c2;
virtual void Test()
{
cout<<"C2::Test()"<<endl;
}
virtual void Test3()
{
cout<<"C2::Test3()"<<endl;
}
};
class D:public C1,public C2
{
public:
int _d;
virtual void Test()
{
cout<<"D::Test()"<<endl;
}
virtual void Test4()
{
cout<<"D::Test4()"<<endl;
}
};
C1和C2的存储模型:
如果在C1或者C2里面没有定义虚函数,那么在C1或者C2里面就没有自己的虚表指针。
D类的模型:
在这里需要注意,这里的偏移量表指针的第一个数据不是0而是-4,因为在这里偏移量表指针的第一个数据是相对于自己的偏移量,在偏移量指针前面还存在着虚表指针。
这里同样的,如果D里面没有自己特有的虚函数,那么D就没有自己的虚表指针。