• 第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;
    }



  • 相关阅读:
    set&enum小结(database)
    bootstrap基础
    看一篇,学一篇,今日份的pandas,你该这么学!No.2
    Python数据分析库之pandas,你该这么学!No.1
    面试Python工程师,这几道编码题有必要背背,Python面试题No8
    周三面试Python开发,这几道Python面试题差点答错,Python面试题No7
    昨天去面试,这5个Python面试题都被考到了,Python面试题No6
    2019年,Python工程师必考的6个面试题,Python面试题No5
    去面试Python工程师,这几个基础问题一定要能回答,Python面试题No4
    学习Python一年,基础忘记了,看看面试题回忆回议,Python面试题No3
  • 原文地址:https://www.cnblogs.com/-glb/p/11967935.html
Copyright © 2020-2023  润新知