多重继承的问题三:
多重继承可能产生多个虚函数表
#include <iostream>
using namespace std;
class BaseA
{
public:
virtual void funcA()
{
cout << "BaseA::funcA()" << endl;
}
};
class BaseB
{
public:
virtual void funcB()
{
cout << "BaseB::funcB()" << endl;
}
};
class Derived : public BaseA, public BaseB
{
};
int main()
{
Derived d;
cout << "sizeof(d)=" << sizeof(d) <<endl;
return 0;
}
sizeof(d) = 8
相关的三个类中都没有定义成员变量,那这8个字节是从哪来的,谁占用的?
虚函数表指针
在Derived这个类中有两个成员,这两个成员都是虚函数表指针。在创建对象的时候,这两个成员会指向不同的虚函数表
#include <iostream>
using namespace std;
class BaseA
{
public:
virtual void funcA()
{
cout << "BaseA::funcA()" << endl;
}
};
class BaseB
{
public:
virtual void funcB()
{
cout << "BaseB::funcB()" << endl;
}
};
class Derived : public BaseA, public BaseB
{
};
int main()
{
Derived d;
BaseA* pa = &d;
BaseB* pb = &d;
BaseB* pbb = (BaseB*)pa;
cout<< "using pa to call funcA()..." <<endl;
pa->funcA(); //对funcA的调用肯定是通过指向虚函数表的指针来完成的
cout << "using pb to call funcB()..." << endl;
pb->funcB() //对funcA的调用肯定是通过指向虚函数表的指针来完成的
cout<< "using pbb to call funcB()..." << endl; pbb->funcB(); //这个地方应该打印funcB(),而打印结果是funcA()
return 0;
}
需要进行强制类型转换时,C++中推荐使用新式类型转换关键字。
解决方案:dynamic_cast
通过pa指针调用funcA的过程:
从pa中得到对象的地址;
通过该地址找到虚函数表指针;
通过虚函数表的指针到虚函数表中去找对应的函数地址。
因此找到的是funcA的地址。
通过pb调用funcB的过程与pa调用funcA的过程是一样的。
通过pbb指针调用funcB的过程:
首先通过pbb得到对象的地址;
然后去找虚函数表指针,找到的虚函数表是vptr1;
去vptr1这个虚函数指针所指的虚函数表中找funcB的地址,在这个地方肯定找不到。找到的是funcA的地址。
原因:原因就是(BaseB* )pa这个地方的强制类型转换是有问题的。在进行强制类型转换时,推荐使用新式类型转换关键字,不要再使用C语言那种暴力的强制转换了
int main()
{
Derived d;
BaseA* pa = &d;
BaseB* pb = &d;
BaseB* pbb = dynamic_cast<BaseB*>(pa);
cout<< "using pa to call funcA()..." <<endl;
pa->funcA();
cout << "using pb to call funcB()..." << endl;
pb->funcB();
cout<< "using pbb to call funcB()..." << endl;
pbb->funcB();
return 0;
}
BaseB* pbb = dynamic_cast<BaseB*>(pa);
此时编译器做了什么呢?
由于使用了dynamic_cast这个关键字,编译器就会去做检查,首先编译器就会检查pa这个对象所指向的对象是什么,发现是d对象。
进而编译器会去看d对象它有哪些父类呢?发现有BaseA和BaseB,然后编译器就会认为这个地方进行的强制类型转换是合法的,那又会怎样呢?编译器在强制类型转换的时候就会对指针有一个修正的过程。使得pbb指向pb所指的位置。
int main()
{
Derived d;
BaseA* pa = &d;
BaseB* pb = &d;
BaseB* pbb = dynamic_cast<BaseB*>(pa);
BaseB* pbc = (BaseB*)pa;
cout<< "using pa to call funcA()..." <<endl;
pa->funcA();
cout << "using pb to call funcB()..." << endl;
pb->funcB();
cout<< "using pbb to call funcB()..." << endl;
pbb->funcB();
cout << "pa=" << pa <<endl;
cout << "pb=" << pb << endl;
cout << "pbb=" << pbb << endl;
cout << "pbc=" << pbc << endl;
return 0;
}