Class sizes with virtual inheritance in C++

假装没事ソ 提交于 2019-12-18 17:05:04

问题


#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.


回答1:


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.




回答2:


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:

  • Using virtual abc http://ideone.com/h5y7R
    • sizeof(xyz) == 44 (88 on 64bit arch)
  • Using nonvirtual abc http://ideone.com/h5y7R
    • sizeof(xyz) == 68 (128 (padded) on 64bit arch)

 

#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.




回答3:


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.




回答4:


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.)




回答5:


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).



来源:https://stackoverflow.com/questions/8105868/class-sizes-with-virtual-inheritance-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!