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:
- 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
Nope.
sizeof(Class) only includes a VTable pointer.
class A
{
public:
int a = 2;
int b = 2;
virtual void x() {
};
virtual void y() {
};
};
class B : public A
{
public:
int c = 2;
int d = 2;
virtual void y() {
};
};
class C : public A
{
public:
int c = 2;
int d = 2;
int e = 2;
virtual void x() {
};
};
So for this example,
cout << sizeof(A)-sizeof(void*) << endl;
cout << sizeof(B)-sizeof(void*) << endl;
cout << sizeof(C)-sizeof(void*) << endl;
should give you the correct answer.
8
16
20