Search code examples
c++classsizeofvtablemembers

Get the sizeof Object's Members


There is an object who's members I need to find the size of. I am specifically asking for the object's size without it's v-table considered. Also, I cannot modify it, so I cannot take advantage of this answer.

Is there a provision for this in C++, beyond summing a hard-coded sizeof for each member?

I am aware that v-tables are not mandated by C++. I am also aware that anything I do with this information will be widely considered "bad form". This question is simply asking if it's possible, not endorsing the behavior.


It has come to my attention that I need to clarify this question. What I wanted to learn with this question was how to cast a parent to a child. That is, I wanted to preserve the child's v-table, but copy the parent's member variables: https://stackoverflow.com/a/31454039/2642059

The accepted answer does provide me the information I needed to do this. But, in-spite of behavior that I consider endemic to the worst of http://stackoverflow.com curiousguy points out a shortcoming of the accepted answer.

The extension from the accepted answer to multiple inheritance is patently obvious, but it is valid that the answer should include it. As a stopgap I've added a live example of how to deal with multiple inheritance: http://ideone.com/1QOrMz I will request that user2596732 updates his answer or I will add a supplementary answer to the question on how to deal with multiple inheritance.


Solution

  • In order to discover the layout of a polymorphic object, you can compare pointers to member objects; the simple demonstration program "draws" the layout of an object with symbols:

    • a lowercase letter is the name of a data member
    • an uppercase letter is the name of a base class
    • * indicates part of the object that do not belong to any member subobject or base class subobject

    There is a symbol for each byte (a char is a byte by definition).

    The vptr(s) must be in the "empty" space, the space not allocated for data members.

    Type definitions are:

    struct T { 
        virtual void foo();
        int i;
    };
    
    struct U { 
        virtual void bar();
        long long l;
    };
    
    struct Der : T, U { 
    };
    
    struct Der2 : virtual T, U { 
    };
    
    struct Der3 : virtual T, virtual U { 
    };
    

    Output is:

    sizeof void* is 4
    sizeof T is 8
    sizeof i is 4
    i is at offset 4
    layout of T is 
    ****iiii
    sizeof U is 12
    sizeof U::l is 8
    l is at offset 4
    layout of U is 
    ****llllllll
    sizeof Der is 20
    Der::i is at offset 4
    Der::l is at offset 12
    Der::T is at offset 0
    Der::U is at offset 8
    layout of Der is 
    TTTTiiiiUUUUllllllll
    sizeof Der2 is 20
    Der2::i is at offset 16
    Der2::l is at offset 4
    Der2::T is at offset 12
    Der2::U is at offset 0
    layout of Der2 is 
    UUUUllllllllTTTTiiii
    sizeof Der3 is 24
    Der3::i is at offset 8
    Der3::l is at offset 16
    Der3::T is at offset 4
    Der3::U is at offset 12
    layout of Der3 is 
    ****TTTTiiiiUUUUllllllll
    

    See https://ideone.com/g5SZwk

    Because we know the compiler is using vptrs, the locations of the vptrs are obvious in these "drawings".

    About inheritance in C++

    Non-virtual inheritance

    When virtual inheritance is not used, the base class subobjects inheritance graph is always a tree rooted in the most derived class, even when the subtyping graph is not a tree:

    struct Repeated {
        virtual void f();
        virtual void g();
    };
    struct Left : Repeated {
        void g();
    };
    struct Right : Repeated {
        void g();
    };
    struct Bottom : Left, Right {
        void f();
    };
    

    The subtyping graph is:

               Left
             /      \
    Repeated          Bottom
             \      /
               Right
    

    The subobject graph is:

    Left::Repeated ---  Left
                             \
                              Bottom
                             /
    Right::Repeated --- Right
    

    This is crucial effect of non-virtual inheritance: the graphs don't always match. If you don't understand that you don't understand non-virtual inheritance!

    This implies that conversions from Bottom* to Repeated* are ambiguous.

    In this example:

    • Bottom::f() overrides both Left::Repeated::f() and Right::Repeated::f() at the same time.
    • Left::Repeated::g() is overridden by Left::g()
    • Right::Repeated::g() is overridden by Right::g()

    Here the lookup of the name g in Bottom would fail with an ambiguity, so it would be an error to use an unqualified g in Bottom.

    Virtual inheritance

    When virtual inheritance is used, the base class subobjects inheritance is an acyclic directed graph with the most derived class as a unique terminal:

    struct Unique { virtual void f(); };
    struct Left : virtual Unique { void f(); };
    struct Right : virtual Unique { void f(); };
    struct Bottom : Left, Right { void f(); };
    

    Here all other f() declarations override Unique::f().

    Here the subobject graph matches the subtype graph:

               Left
             /      \
      Unique         Bottom
             \      /
               Right