第53课 被遗弃的多重继承 (中)

多重继承的问题三:
多重继承可能产生多个虚函数表

#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;
}



猜你喜欢

转载自www.cnblogs.com/-glb/p/11967935.html