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.
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:
*
indicates part of the object that do not belong to any member subobject or base class subobjectThere 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
Because we know the compiler is using vptrs, the locations of the vptrs are obvious in these "drawings".
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
.
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