zoukankan      html  css  js  c++  java
  • C++ 菱形虚拟继承 与 指针偏移问题

    首先在谈到菱形虚拟继承之前先说明一下菱形继承

    菱形继承是多继承的一种特殊情况(如下,画渣勿喷):

    C++ 菱形虚拟继承 与 指针偏移问题

    图中 B C 两个类都继承了A类,而 B C 又都被 D类继承
    按照继承的定义,派生类当中都包含了基类,而这时虚拟继承这种情况就会产生问题

    <a>数据冗余
    首先是按照虚拟继承的这种方法,D类当中就包含了两份A类数据(分别来自B类和C类)
    <b>二义性
    其次是,当我们通过D类去访问A类中的数据时,就会产生二义性,编译器不知道是要去访问D类中B中包含的A类数据还是C中包含的A类数据。

    这时就要引入菱形虚拟继承来了,但其实二义性的问题即使不使用菱形虚拟继承也是可以解决的,相反数据冗余不行:

    class A{
    public:
        int aa = 0;
    };
    class B : public A{
    };
    class C : public A{};
    class D : public B, public C{};
    
    int main(){
        D d1; 
        //d1.aa = 10;按照以上定义,如果运行这一句就会出现aa不明确的错误
        //若是像以下这样显示指定则没有问题
        d1.C::aa = 10;
        d1.B::aa = 20;
        return 0;
    }

    但是既然数据冗余的问题解决不了,我们还是需要使用菱形虚拟继承,即如下代码:

    class A{
    public:
        int aa = 0;
    };
    class B : virtual public A{};
    class C : virtual public A{};
    class D : public B, public C{};
    int main(){
        D d1; 
        d1.aa = 10; //此时执行这一句就不会有问题
        return 0;
    }

    当aa复制成功后的监视窗口如下

    C++ 菱形虚拟继承 与 指针偏移问题

    (D的对象内存模型↑)
    没有采用菱形虚拟继承前,从内存模型中看可以很明显看出存在两份A的数据,自然会有数据冗余和二义性的问题

    但是菱形虚拟继承从内存模型中看,B和C在虚拟继承A时所存储的不是A的数据,而是A的地址偏移量的地址,这两个指针叫虚基表指针,两个表叫虚基表。虚基表中存的就是偏移量。通过偏移量便可以找到下面的A。流程就是:当访问到A中数据时,首先通过偏移量地址找到相对当前位置A的地址偏移量,然后通过地址偏移量再找到A的数据,这样就解决了两大问题。
    这里有两个需要关注的点:一是虚拟继承主要就是用来解决菱形继承问题,其他地方不可乱用。
    二是一般不建议设计出多继承,一定不要设计出菱形继承,否则程序在复杂性和性能上会有较大问题(多继承可以认为是C++的缺陷之一,所以后来很多语言都舍弃了多继承,比方说JAVA)

    ====================================================================

    现在再来说多继承指针漂移的问题
    先来看一段代码

    class A{
    public:
        int aa = 0;
    };
    class B{
    public:
        int bb = 0;
    };
    class C :public A, public B{
    public:
        int cc = 0;
    };
    
    int main(){
        C p;
        A* a1 = &p;
        B* b1 = &p;
        C* c1 = &p;
        cout << a1 << ' ' << b1 << ' ' << c1 << endl;
        if (b1 == c1){
            cout << "b1 = c1" << endl;
        }
        return 0;
    }

    不了解这个问题的人,肯定会觉得a1,b1,c1三个指针的值应该是一样的,但事实上↓

    C++ 菱形虚拟继承 与 指针偏移问题

    这个问题就很玄乎了,赋的是同样的值,为什么b1和c1的值会不同呢?但是为什么下面的if判断却又判断他两相同呢?

    首先是第一个问题,导致地址不同的原因是 在C进行继承时有一个先后顺序(按照代码的编写顺序),他会先继承A类,再继承B类,最后再实现自己的部分,这就导致了,a1指向了C对象中A的位置,b1指向了C对象中B的位置,从而导致了地址不同。

    但为什么 if 判断会通过呢?这是第二个问题,当判断 ‘==’ 时,会分析两个地址是不是指向了同一个实例对象,如果是,就会做隐式的类型转换 ,然后再判等(虽然地址不同但指向的是同一个实例对象)。这就导致这样的结果。

    多继承指针漂移和虚拟继承都是C++继承部分需要知道的点,最好还是加以记忆。

  • 相关阅读:
    切片器化繁为简,盘它 !
    微软连续12年成为Gartner分析和BI平台魔力象限的领导者
    2019微软Power BI 每月功能更新系列——2月Power BI 新功能学习
    DAX创建带有过滤器的超链接
    屏蔽flash地区识别
    关于Windows自动化卸载软件的思路
    拒绝后门程序-Alibabaprotect和AliPaladin
    二叉树的三种遍历
    [灯火阑珊] 关于cmd命令里的findstr匹配多个关键词
    递归函数详解——VS调试教你理解透彻递归
  • 原文地址:https://www.cnblogs.com/Kaniso-Vok/p/13756219.html
Copyright © 2011-2022 走看看