• (转)虚函数和虚拟继承的内存模型


    转自:http://blog.csdn.net/llingy/article/details/5868337

    (1)单一的一般继承 (带成员变量、虚函数、虚函数覆盖)

         1)虚函数表在最前面的位置。

         2)成员变量根据其继承和声明顺序依次放在后面。

         3)在单一的继承中,被 overwrite 的虚函数在虚函数表中得到了更新 。

    (2)多重继承 (带成员变量、虚函数、虚函数覆盖)

         1)   每个父类都有自己的虚表。

         2)   子类的成员函数被放到了第一个父类的表中。

         3)   内存布局中,其父类布局依次按声明顺序排列。

         4)   每个父类的虚表中的 f()函数都被 overwrite成了子类的 f() 。这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。

    (3)重复多重继承 (带成员变量、虚函数、虚函数覆盖)

       这个和上面一样,只不过最顶层的基类在二级子类里有两份同样的拷贝,在直接访问顶层基类的 成员变量时,会出现二义性。

       d.ib = 0;             //二义性错误

       d.B1::ib = 1;           //正确

       d.B2::ib = 2;           //正确

    (4)单一的虚拟继承 (带成员变量、虚函数、虚函数覆盖)

    (5)钻石型的虚拟多重继承 (带成员变量、虚函数、虚函数覆盖)

            第4和第5种情况就比较复杂了,因为这个内存布局其实是和具体的编译器相关的,不同的编译器实现不一样。

            我们可以参照教材《inside c++ object model》P121和P123中的介绍。分别是使用pointer strategy和virtual table offset strategy.

            在单一虚拟继承时:

          (1)VS编译器:无论有无虚函数,必然含有虚基类表指针。虚基类表中的内容为本类实例的偏移和基类实例的相对偏移值。如果有虚函数,那么基类的虚函数表跟派生类的虚函数表是分开的。

          (2)GNU的GCC编译器:跟VS的编译器类似,有不同的地方是,虚基类表跟派生类的虚函数表合并。另外通过虚基类表指针往正负两个方向寻址,可以获得不同偏移值,也就是说有两个功能一样的虚函数表 。

           最后总结一下虚基类表的问题:

             VS编译器会去使用虚基类表,用于寻址虚基类地址(virtual base class table strategy )。而GCC编译器则没有这么做,而是直接在派生类对象地址上加上一个常数,获得虚基类实例的地址(virtual table offset strategy .)。

           有一个很好的例子:

    #include <stdio.h>

    class A
    {
    public:
        virtual int foo0(){
            return 0;
        }
        char a[3];
    };

    class B: virtual public  A
    {
    public:
        virtual int foo1(){
            return 1;
        }
        char b[3];
    };

    class C:virtual public  B
    {
    public:
        virtual int foo2(){
            return 2;
        }
        char c[3];
    };

    int main(int argc, char* argv[])
    {
        A a;
        B b;
        C c;

        printf(" sizeof A=%d, sizeof B=%d, sizeof C=%d /n",sizeof (A), sizeof (B), sizeof (C));
        return 0;
    }

    在g++编译器里: sizeof A=8, sizeof B=16(A和B都有虚函数表,但B的虚函数表的负方面的地址里存着virtual base class(A)offset), sizeof C=24(C的虚函数表的负方面的地址里存着virtual base class(B)offset)

    在VC++编译器里: sizeof A=8, sizeof B=20(A和B都有虚函数表,并且B还有一个virtual class base table pointer), sizeof C=32

  • 相关阅读:
    python求余、除法运算、向下圆整、round圆整
    【转】从入门到实践 json练习详解~~和ython : groupby 结果浅解,&之后的 y_list=[v for _,v in y]
    ### 模块“*.dll”已加载,但对DllRegisterServer的调用失败,错误代码为0x80070005
    python从excel里读取数据
    文本文件和二进制文件的区别
    析构函数 声明为protected
    c语言中ln,lg,log的表示。c语言中ln,lg,log的表示。
    js设计模式--创建型--单例模式
    js设计模式--创建型--工厂模式
    解决ElementUI的table组件在flex布局下宽度不能自适应的问题
  • 原文地址:https://www.cnblogs.com/yysblog/p/2783402.html
Copyright © 2020-2023  润新知