在之前的博客中有简单的介绍了一下:没有虚函数的菱形继承。此篇博客将对菱形继承进行升级,介绍有虚函数的菱形继承。
举一个简单的例子:
#include<iostream>
using namespace std;
class AA
{
public:
virtual void fun1()
{
cout << "AA::fun1()" << endl;
}
virtual void fun2()
{
cout << "AA::fun2()" << endl;
}
virtual void fun3()
{
cout << "AA::fun3()" << endl;
}
protected:
int _aa;
};
class BB : public AA
{
public:
virtual void fun1()
{
cout << "BB::fun1()" << endl;
}
virtual void fun2()
{
cout << "BB::fun2()" << endl;
}
protected:
int _bb;
};
class CC : public AA
{
public:
virtual void fun1()
{
cout << "CC::fun1()" << endl;
}
virtual void fun2()
{
cout << "CC::fun2()" << endl;
}
protected:
int _cc;
};
class DD : public BB, public CC
{
public:
virtual void fun1()
{
cout << "DD::fun1()" << endl;
}
virtual void fun2()
{
cout << "CC::fun2()" << endl;
}
virtual void fun4()
{
cout << "DD::fun4()" << endl;
}
protected:
int _dd;
};
typedef void (*FUNC)();
void PrintVTable(int* VTable)
{
printf("0x%p\n", VTable);
for (int i = 0; VTable[i] != 0; ++i)
{
FUNC f = (FUNC)VTable[i];
f();
}
}
int main()
{
DD dd;
int* VTable = (int*)(*(int*)&dd);
PrintVTable(VTable);
return 0;
}
监视窗口能够看出菱形继承里面的内存布局如下:
通过监视窗口我们能够看到dd中有继承的类BB和类CC,其中BB和CC中都各自包括一个虚表指针,且指针的地址不相同,则两个类各自存在一个虚函数表。同时我们也能够看到菱形继承中,dd中存在两个成员_aa,这就存在问题,当我们想要访问_aa时,编译器不知道我们想要访问的是哪一个变量,这就说明菱形继承存在二义性和数据冗余。那么如何解决菱形继承所存在的问题呢?
菱形虚拟继承解决了菱形继承中存在的二义性和数据冗余。虚拟继承就是增加虚基表和偏移量,将BB和CC继承改为虚拟继承。
代码如下:
#include<iostream>
using namespace std;
class AA
{
public:
virtual void fun1()
{
cout << "AA::fun1()" << endl;
}
virtual void fun2()
{
cout << "AA::fun2()" << endl;
}
virtual void fun3()
{
cout << "AA::fun3()" << endl;
}
protected:
int _aa;
};
class BB : virtual public AA
{
public:
virtual void fun1()
{
cout << "BB::fun1()" << endl;
}
virtual void fun2()
{
cout << "BB::fun2()" << endl;
}
protected:
int _bb;
};
class CC : virtual public AA
{
public:
virtual void fun1()
{
cout << "CC::fun1()" << endl;
}
virtual void fun2()
{
cout << "CC::fun2()" << endl;
}
protected:
int _cc;
};
class DD : virtual public BB, virtual public CC
{
public:
virtual void fun1()
{
cout << "DD::fun1()" << endl;
}
virtual void fun2()
{
cout << "CC::fun2()" << endl;
}
virtual void fun4()
{
cout << "DD::fun4()" << endl;
}
protected:
int _dd;
};
typedef void (*FUNC)();
void PrintVTable(int* VTable)
{
printf("0x%p\n", VTable);
for (int i = 0; VTable[i] != 0; ++i)
{
FUNC f = (FUNC)VTable[i];
f();
}
}
int main()
{
DD dd;
int* VTable = (int*)(*(int*)&dd);
PrintVTable(VTable);
return 0;
}
通过监视窗口,我们可以看到以下现象:
菱形虚拟继承比菱形继承的dd中多存在一个指针_vfptr,该指针指向偏移量。至此,菱形继承消除了其存在的二义性及数据冗余。