Multiple Inheritance : size of class for virtual pointers?

后端 未结 4 1084
星月不相逢
星月不相逢 2020-12-06 19:11

Given the code:

class A{};

class B : public virtual A{};

class C : public virtual A{};

class D : public B,public C{};

int main(){
cout<<\"sizeof(D)         


        
相关标签:
4条回答
  • 2020-12-06 19:43

    First: without virtual functions, it's probable that there isn't a vptr at all in the classes. The 8 bytes you're seeing are an artifact of the way virtual inheritance is implemented.

    It's often possible for several classes in a hierarchy to share the same vptr. For this to occur, it is necessary for their offset in the final class to be the same, and for the list of vtable entries in the base class to be an initial sequence the list of vtable entries in the derived class.

    Both conditions are met in almost all implementations for single inheritance. No matter how deep the inheritance, there will usually be only one vptr, shared between all of the classes.

    In the case of multiple inheritance, there will always be at least one class for which these requirements aren't met, since the two base classes can't have a common start address, and unless they have exactly the same virtual functions, only one's vtable could possibly be an initial sequence of the other.

    Virtual inheritance adds another quirk, since the position of the virtual base relative to the class inheriting from it will vary depending on the rest of the hierarchy. Most implementations I've seen use a separate pointer for this, although it should be possible to put this information in the vtable as well.

    If we take your hierarchy, adding virtual functions so that we are certain of having a vptr, we notice that B and D can still share a vtable, but both A and C need separate vtables. This means that if your classes had virtual functions, you would need at least three vptr. (From this I conclude that your implementation is using separate pointers to the virtual base. With B and D sharing the same pointer, and C with its own pointer. And of course, A doesn't have a virtual base, and doesn't need a pointer to itself.)

    If you're trying to analyse exactly what is going on, I'd suggest adding a new virtual function in each class, and adding a pointer sized integral type that you initial with a different known value for each class. (Use constructors to set the value.) Then create an instance of the class, take it's address, then output the address for each base class. And then dump the class: the known fixed values will help in identifying where the different elements lie. Something like:

    struct VB
    {
        int vb;
        VB() : vb( 1 ) {}
        virtual ~VB() {}
        virtual void fvb() {}
    };
    
    struct Left : virtual VB
    {
        int left;
        Left() : left( 2 ) {}
        virtual ~Left() {}
        virtual void fvb() {}
        virtual void fleft() {}
    };
    
    struct Right : virtual VB
    {
        int right;
        Right() : right( 3 ) {}
        virtual ~Right() {}
        virtual void fvb() {}
        virtual void fright() {}
    };
    
    struct Derived : Left, Right
    {
        int derived;
        Derived() : derived( 5 ) {}
        virtual ~Derived() {}
        virtual void fvb() {}
        virtual void fleft() {}
        virtual void fright() {}
        virtual void fderived() {}
    };
    

    You might want to add a Derived2, which derives from Derived and see what happens to the relative addresses between e.g. Left and VB depending on whether the object has type Derived or Derived2.

    0 讨论(0)
  • 2020-12-06 19:44

    It depends on compiler implementation. My compiler is Visual Stdio C++ 2005.

    Code like this:

    int main(){
        cout<<"sizeof(B):"<<sizeof(B) << endl;
        cout<<"sizeof(C):"<<sizeof(C) << endl;
        cout<<"sizeof(D):"<<sizeof(D) << endl;
        return 0;
    } 
    

    It will output

    sizeof(B):4
    sizeof(C):4
    sizeof(D):8
    

    class B has only one virtual pointer. So sizeof(B)=4. And class C is also.

    But D multiple inheritance the class B and class C. The compile don't merge the two virtual table.So class D has two virtual pointer point to each virtual table.

    If D only inheritance one class and not virtual inheritance. It will merge they virtual table.

    0 讨论(0)
  • 2020-12-06 19:47

    You are making far too many assumptions. This is highly dependent on the ABI, so you should look into the documentation for your platform (my guess is that you are running on a 32bit platform).

    The first thing is that there are no virtual functions in your example, and that means that none of the types actually contains a pointer to the virtual table. So where did those 2 pointers come from? (I am assuming you are on a 32bit architecture). Well, virtual inheritance is the answer. When you inherit virtually, the relative location of the virtual base (A) with respect to the extra elements in the derived type (B,C) will change along the inheritance chain. In the case of a B or C object the compiler can lay the types as [A,B'] and [A,C'] (where X' is the extra fields of X not present in A).

    Now virtual inheritance means that there will only be one A subobject in the case of D, so the compiler can layout the D type as [A,B',C',D] or [A,C',B',D] (or any other combination, A might be at the end of the object, etc, this is defined in the ABI). So what does this imply, this implies that member functions of B and C cannot assume where the A subobject might be (in the event of non-virtual inheritance, the relative location is known), because the complete type might actually be some other type down the chain.

    The solution to the problem is that both B and C usually contain an extra pointer-to-base pointer, similar but not equivalent to the virtual pointer. In the same way that the vptr is used to dynamically dispatch to a function, this extra pointer is used to dynamically find the base.

    If you are interested in all this details, I recommend that you read the Itanium ABI, which is widely used not only in Itanium but also in other Intel 64 architectures (and a modified version in 32 architectures) by different compilers.

    0 讨论(0)
  • 2020-12-06 19:56

    It depends on compiler implementation so you should specify which compiler you're using. Anyway D derives from two classes so it contains pointers to B and C vtables base class pointers (I don't know a good name for this).

    To test this you may declare a pointer to B and a pointer to C and cast the address of D to base class pointer. Dump that values and you'll see they're different!

    EDIT
    Test made with Visual C++ 10.0, 32 bit.

    class Base
    {
    };
    
    class Derived1 : public virtual Base
    {
    };
    
    class Derived2 : public virtual Base
    {
    };
    
    class Derived3 : public virtual Base
    {
    };
    
    class ReallyDerived1 : public Derived1, public Derived2, public Derived3
    {
    };
    
    class ReallyDerived2 : public Derived1, public Derived2
    {
    };
    
    class ReallyDerived3 : public Derived2
    {
    };
    
    void _tmain(int argc, _TCHAR* argv[])
    {
     std::cout << "Base: " << sizeof(Base) << std::endl;
     std::cout << "Derived1: " <<  sizeof(Derived1) << std::endl;
     std::cout << "ReallyDerived1: " <<  sizeof(ReallyDerived1) << std::endl;
     std::cout << "ReallyDerived2: " <<  sizeof(ReallyDerived2) << std::endl;
     std::cout << "ReallyDerived3: " <<  sizeof(ReallyDerived3) << std::endl;
    }
    

    Output, guess, is not surprising:

    • Base: 1 byte (OK, this is a surprise, at least for me).
    • Derived1: 4 bytes
    • ReallyDerived1: 12 bytes (4 bytes per base class because of multiple inheritance)
    • ReallyDerived2: 8 bytes (as guessed)
    • ReallyDerived3: 4 bytes (just one base class with virtual inheritance in the path but this is non virtual).

    Adding a virtual method to the base you get 4 bytes more for each class. So probably extra bytes aren't vtable pointers but base class pointers used in multiple inheritance, this behavior does not change removing virtual inheritance (but if not virtual the size doesn't change adding more bases).

    0 讨论(0)
提交回复
热议问题