Hassedev
Hassedev

Reputation: 631

Derived class is bigger than base class even though it consists of reference variables

From what I've read so far, it seems that reference variables are not supposed to take any memory at all. Instead they are treated as the exact same variable they are referencing but with another name.

However, when I ran the following code, it seems that it is not always the case:

#include <cstdio>
struct A
{
    int m[3];
};
struct B: A
{
    B():x(m[0]), y(m[1]), z(m[2]){}
    int& x;
    int& y;
    int& z;
};
int main(){
    printf("%u, %u\n", sizeof(A), sizeof(B));
    return 0;
}

Output:

12, 40

Why is B so much bigger than A?

Is there any other way I can access, for example, B.m[0] with B.x?

Upvotes: 3

Views: 547

Answers (3)

Arne Mertz
Arne Mertz

Reputation: 24606

From what I've read so far, it seems that reference variables are not supposed to take any memory at all.

You should read further ;) Seriously, references are not magic. So in the real world, somehow the object has to store the information about what object the reference is bound to. So while conceptually a reference has no size, it practically is very much like a pointer, and normally the compiler just uses pointers. The behavior that differs from that of pointers (not null, not reassignable, no need to dereference it) is enforced by the compiler at compile time.

So what you see is essentially the size of A plus padding plus the size of three pointers. I guess you're on a 64bit system where sizeof(int) is 4 and sizeof(void*) is 8:

 12 bytes for the A subobject (4 for each int)
+ 4 bytes padding (to get to a multiple of 8 bytes)
+24 bytes for the 3 references/pointers in B (8 for each one)
--------------
 40 bytes total

For your other question, given an object b of type B, you can access b.m[0] directly as it is public in A and publicly inherited. Giving it another name without the reference overhead you encountered is not possible.

Upvotes: 7

Ian Medeiros
Ian Medeiros

Reputation: 1776

You are interpreting things wrong. A reference that is used purely locally can - and often is - eliminated by the optimizer. Note that we still need to store it somewhere (like in a register) during it lifespan.

The compiler can't predict the lifespan of your object as easily as a local variable, so, a member reference can't actually be just replaced and thrown away: it's necessary to hold a pointer (4bytes or 8bytes) to the actual content to keep the member consistent during the object lifespan.

Upvotes: 4

John Zwinck
John Zwinck

Reputation: 249253

A reference is like a pointer which cannot be null and can never be "reseated" (made to point to something other than what it initially pointed to). Therefore we might expect a reference to take the same amount of space as a pointer (though this is not guaranteed). So three ints, 4 bytes for alignment, then three 8-byte pointers would be 40 bytes, making your example perfectly reasonable.

You could make a method like B.x() to return a value without taking up more space. But since C++ lacks "properties" you cannot make B.x return something that takes no space (i.e. you cannot make something that looks like member variable access but behaves like a method call).

Upvotes: 2

Related Questions