C++ 中具有虚拟继承的类大小

发布于 2024-12-14 20:46:28 字数 507 浏览 3 评论 0 原文

#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 表或类似的东西,但不确定。

#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;
}

The answers would be compiler dependent but I'm surprized when I saw this as the result

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

4
16

If I remove the virtual keyword then the size allocated is 4 and 8 respectively which is what I expected.

Why is the extra space being taken up exactly?
I suspect it is for the vptr table or something of that sorts but don't know for certain.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

以可爱出名 2024-12-21 20:46:28

这是一篇关于 GCC 中的虚拟继承和多重继承的好文章 (互联网档案永久链接)

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 字节差异进行疯狂猜测:我可能会调查您的编译器的虚拟继承实现是否将所有虚拟基类视为具有虚拟方法,即使它们没有?

但我几乎弥补了这一点。如果你想检验这个理论,你就必须进一步深入研究。它依赖于实现。

A good article on virtual and multiple inheritance in GCC is this one (Internet Archive Permalink):

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

Yet it doesn't quite answer your question, as you are getting an output of 20 bytes out of whatever (unspecified) compiler and build settings you are using.

If you were using GCC (under the default settings IDEone uses, at least), then you would be getting 12 bytes. Which is the same thing as what it would give had you written:

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

Were you to virtually inherit from abc when it contains virtual methods:

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

...then you would get 16 bytes out of GCC.

Why is the extra space being taken up exactly? I suspect it is for the vptr table or something of that sorts but don't know for certain.

If I had to make a wild guess about your 16 byte variance: I might look into if your compiler's implementation of virtual inheritance treats all virtual base classes as if they had virtual methods, even if they didn't?

But I pretty much made that up. You'll have to look further under the hood if you want to test the theory; it's implementation-dependent.

兰花执着 2024-12-21 20:46:28

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

言归正传,这里有一个反例,可以显示正在发生的事情:

#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 上),您会注意到大小显着减小。另外,请注意,将(任何)intermeditate 基类标记为(非)虚拟时不会产生任何影响。

Virtual bases classes increase (dynamic, runtime) conversion requirements, and I suppose that the size increase are for a kind of 'pivoting ground' to walk the (base) class hierarchy in non-ambiguous ways when doing such conversions.

In less mumbo jumbo, here is a counter example that could show what's going on:

 

#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;
}

You'll notice a significant reduction in size when marking the abc base as virtual (at least on gcc). Also, note no effect when marking (any) of the intermediate base classes as (non)virtual.

只等公子 2024-12-21 20:46:28

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

With virtual base classes, the position of the base object relative to an instance of the derived object is not always the same, so there is a pointer that tracks that.

攒眉千度 2024-12-21 20:46:28

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

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&,以获得更小的类。)

Non-virtual inheritance is just like object containment, given:

struct Derived : Base

It can be compiled to C++ just this way:

struct Derived {
    Base __base;
    // other members

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

Virtual inheritance is like adding a level of indirection, given

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

This can be compiled to C++ as

// 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) {
    }
};

You get the idea...

Note: I have not described the vtable pointer member. (It can be used instead of the Base&, to have smaller classes.)

笙痞 2024-12-21 20:46:28

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

The program when run in CodeBlocks Compiler gives 4 12 as output
This can be explained as; for base class size of object is equal to size of int.
For derived class; size of object = size of the class + size if base class + size of virtual base pointer(4).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文