6
#include<iostream>

using namespace std;

class abc
{
    int a;
};
class xyz : public virtual abc
{
    int b;
};

int main()
{
    abc obj;
    xyz obj1;
    cout<<endl<<sizeof(obj);
    cout<<endl<<sizeof(obj1);
    return 0;
}

答案将取决于编译器,但当我看到结果时我很惊讶

~/Documents/workspace/tmp ‹.rvm-›  $ ./class_sizes   

4
16

如果我删除 virtual 关键字,那么分配的大小分别为 4 和 8,这是我所期望的。

为什么要占用额外的空间?我怀疑它是用于 vptr 表或类似的东西,但不确定。

4

5 回答 5

7

一篇关于 GCC 中虚拟和多重继承的好文章是这篇(Internet Archive Permalink)

http://phpcompiler.org/articles/virtualinheritance.html

然而,它并不能完全回答您的问题,因为您从正在使用的任何(未指定)编译器和构建设置中获得了 20 字节的输出。

如果您使用的是 GCC (至少在 IDEone 使用的默认设置下),那么您将获得 12 个字节。这与您写的内容相同:

class abc
{
    int a;
    virtual void foo() {}
};
class xyz : public abc
{
    int b;
};

当它包含虚拟方法时,您是否虚拟地从 abc 继承:

class abc
{
    int a;
    virtual void foo() {}
};
class xyz : virtual public abc
{
    int b;
};

...然后你会从 GCC 中得到 16 个字节。

为什么要占用额外的空间?我怀疑它是用于 vptr 表或类似的东西,但不确定。

如果我不得不对您的 16 字节差异做出疯狂的猜测:我可能会调查您的编译器的虚拟继承实现是否将所有虚拟基类都视为具有虚拟方法,即使它们没有?

但我几乎弥补了这一点。如果您想测试该理论,则必须进一步研究。它依赖于实现。

于 2011-11-12T16:47:48.263 回答
3

虚拟基类增加(动态,运行时)转换要求,我认为大小增加是为了在进行此类转换时以明确的方式遍历(基)类层次结构的一种“旋转地面”。

在不那么胡说八道的情况下,这里有一个反例可以显示正在发生的事情:

 

#include<iostream>

class abc
{
    int x;
    virtual void t();
};

template <int unique> struct interm : virtual abc 
{
    virtual void t();
    virtual void s();
};

struct xyz : 
    /*virtual*/ interm<1>, 
    /*virtual*/ interm<2>, 
    /*virtual*/ interm<3>, 
    /*virtual*/ interm<4>,
    /*virtual*/ interm<5>, 
    /*virtual*/ interm<6>, 
    /*virtual*/ interm<7>, 
    /*virtual*/ interm<8>
{
    int b;
    virtual void t();
    virtual void s();
};


int main()
{
    std::cout << sizeof(abc)       << std::endl;
    std::cout << sizeof(interm<1>) << std::endl;
    std::cout << sizeof(xyz)       << std::endl;
    return 0;
}

abc将基础标记为虚拟时(至少在 gcc 上),您会注意到大小显着减小。此外,请注意将(任何)interm编辑基类标记为(非)虚拟时没有效果。

于 2011-11-12T16:45:42.200 回答
2

对于虚拟基类,基对象相对于派生对象实例的位置并不总是相同的,因此有一个指针可以跟踪它。

于 2011-11-12T16:50:32.147 回答
2

非虚拟继承就像对象包含,给定:

struct Derived : Base

可以这样编译成C++:

struct Derived {
    Base __base;
    // other members

    // implementation of Derived-to-Base pointer conversion
    operator Base& () { return __base; }
};

虚拟继承就像添加一个间接级别,给定

struct Base
struct L : virtual Base
struct R : virtual Base
struct Derived : L, R

这可以编译为 C++

// the type L& is translated to __L_subobject&
// the type L* is translated to __L_subobject*
// lvalue of L is translated to lvalue of __L_subobject
struct __L_subobject {
    Base &__base_ref;
    __L_subobject (Base &__base_ref) 
        : __base_ref(__base_ref) {
    }
    // other members

    // pointer conversions:
    operator Base& () { return __base_ref; }
};

// a definition of variable x of type L is translated to one with type __L_complete
// but any lvalue x is translated to x.__most_derived
// (it is assumed that rvalues have been already been translated to lvalues)
struct __L_complete {
    // all virtual bases:
    Base __base;

    // derived partial subobjects:
    __L_subobject __most_derived;

    __L_complete () : __most_derived(__base) {}
};

// ... same for R ...

struct __Derived_subobject {
    __L_subobject __L;
    __R_subobject __R;
    // other members ...

    __Derived_subobject (Base &__base_ref) 
        : __L(__base_ref),
          __R(__base_ref) {
    }

    // pointer conversions:
    operator Base& () { return __L.operator Base& (); }
    operator __L_subobject& () { return __L; }
    operator __R_subobject& () { return __R; }
};

struct __Derived_complete {
    // all virtual bases:
    Base __base;

    // derived partial subobjects:
    __Derived_subobject __most_derived;

    __Derived_complete () :__most_derived(__base) {
    }
};

你明白了……

注意:我没有描述 vtable 指针成员。(它可以用来代替Base&, 来拥有更小的类。)

于 2011-12-26T05:46:01.610 回答
0

程序在 CodeBlocks Compiler 中运行时会给出 4 12 作为输出 这可以解释为;对于基类对象的大小等于 int 的大小。对于派生类;对象的大小 = 类的大小 + 基类的大小 + 虚拟基指针的大小 (4)。

于 2013-06-20T09:29:49.783 回答