加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c – 有关Virtual Inheritance层次结构的问题

发布时间:2020-12-16 09:27:31 所属栏目:百科 来源:网络整理
导读:在处理虚拟继承时遇到这个问题.我记得在非虚拟继承层次结构中,子类的对象拥有其直接超类的对象.那么虚拟继承呢?在这种情况下,子类的对象是直接拥有其超类的对象还是仅仅持有指向其超类对象的指针? 顺便说一下,为什么以下代码的输出是: sizeof(A): 8sizeof
在处理虚拟继承时遇到这个问题.我记得在非虚拟继承层次结构中,子类的对象拥有其直接超类的对象.那么虚拟继承呢?在这种情况下,子类的对象是直接拥有其超类的对象还是仅仅持有指向其超类对象的指针?

顺便说一下,为什么以下代码的输出是:

sizeof(A): 8
sizeof(B): 20
sizeof(C): 20
sizeof(D): 36

码:

#include <iostream>

using namespace std;

class A{
    char k[ 3 ];
    public:
        virtual void a(){};
};

class B : public virtual A{
    char j[ 3 ];
    public:
        virtual  void b(){};
};

class C : public virtual A{
    char i[ 3 ];
    public:
        virtual void c(){};
};

class D : public B,public C{
    char h[ 3 ];
    public:
        virtual void d(){};
};

int main( int argc,char *argv[] ){
    cout << "sizeof(A): " << sizeof( A ) << endl;
    cout << "sizeof(B): " << sizeof( B ) << endl;
    cout << "sizeof(C): " << sizeof( C ) << endl;
    cout << "sizeof(D): " << sizeof( D ) << endl;

    return 0;
}

提前致谢.
亲切的问候.

解决方法

虚拟基础对象位于内存块中属于对象的某个位置(具有size = sizeof(object)的内存).因为不同类型的若干子对象可以以各种方式组合但必须共享相同的基础对象,所以每个子对象需要偏移指针来找出虚拟基础对象.如果没有虚拟继承,则在编译时为每个类类型修复找出相应基础对象的偏移量.

sizeof值取决于您的编译器和机器,但以下假设非常常见:

假设:指针大小为4个字节

假设:类大小向上舍入为4个字节的倍数

sizeof(A): 8  ->   1 pointer to vtable (virtual method) 
                 + 3 chars -> 4+3=7 
              -> round up to 8

sizeof(B): 20 ->   8 + 1 pointer to vtable (virtual method) 
                 + 1 offset pointer to virtual base 
                 + 3 chars -> 8 + 4 + 4 + 3 = 19 
              -> round up to 20

sizeof(C): 32 ->  20 + 1 pointer to vtable (virtual method) 
                 + 1 offset pointer to virtual base 
                 + 3 chars 
              -> 20 + 4 + 4 + 3 = 31 // this calculation refers to an older 
              -> round up to 32      // version of the question's example 
                                     // where C had B as base class

猜测计算是因为实际计算必须完全知道编译器的工作原理.

问候,
奥利弗

更多细节为什么需要额外的偏移指针:

例:

class B  : virtual public A {...};
class C  : virtual public A {...};
class D1 : public B {...};
class D2 : public B,C {...};

D1的可能内存布局:

A
B
D1

D2的可能内存布局:

A
C
B
D2

在第二种情况下,子对象B需要另一个偏移量来找到它的基数A.

D2类型的对象由一个内存块组成,其中包含所有父对象部分,即D2类型对象的内存块有一个A成员变量,C成员变量,B成员变量和D2成员变量.这些部分的顺序取决于编译器,但是示例显示,对于多个虚拟继承,需要一个偏移指针,该指针指向对象的总内存块中的虚拟基础对象.这是必需的,因为B类的方法只知道一个指向B的指针,并且必须以某种方式计算A存储器部分相对于t??his指针的位置.

计算尺寸(D):

sizeof(D): 36 ->   A:3 chars + A:vtable 
                 + B:3 chars + B:vtable + B:virtual base pointer
                 + C:3 chars + C:vtable + C:virtual base pointer
                 + D:3 chars + D:vtable
               =   3 + 4 
                 + 3 + 4 + 4 
                 + 3 + 4 + 4 
                 + 3 + 4 
                 = 36

上面的计算可能是错的;-) ……

我不确定D部分是否有自己的vtable指针(这都是高度依赖编译器的).

我现在认为可能是D部分使用其父类的vtable指针条目,并且4个额外字节用于对齐每个部分(8个字节的倍数):

所以这个计算可能更正确:

sizeof(D): 36 ->   A:3 chars + A:vtable + A:alignment
                 + B:3 chars + B:vtable + B:virtual base pointer + B:alignment
                 + C:3 chars + C:vtable + C:virtual base pointer + C:alignment
                 + D:3 chars + D:alignment
               =   3 + 4 + 1
                 + 3 + 4 + 4 + 1 
                 + 3 + 4 + 4 + 1
                 + 3 + 1
                 = 36

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读