3

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?

Hassedev
  • 621
  • 6
  • 17
  • 6
    You’ve read wrong. Mystery solved. ;-) – Konrad Rudolph Apr 17 '13 at 13:54
  • http://stackoverflow.com/questions/1179937/how-does-a-c-reference-look-memory-wise I read it from there, among other sources. – Hassedev Apr 17 '13 at 13:58
  • 4
    @Hassedev That has to do with references on the stack for functions, references that are members are a different beast – Dan F Apr 17 '13 at 13:59
  • 4
    Than the author of that answer was a bit careless in their wording, and you still haven't read carefully enough. Any reference **will** require memory by default. But that **may** be optimized away in some cases. – StoryTeller - Unslander Monica Apr 17 '13 at 14:02
  • Aaaand... Now that I read it again, I noticed that they may be implemented many different ways. Well, the other question is still valid :) – Hassedev Apr 17 '13 at 14:03
  • Well, you can `#define x m[0]` in the context of the class's implementation file... – StoryTeller - Unslander Monica Apr 17 '13 at 14:07
  • I do however wonder why you'd want to do that? – StoryTeller - Unslander Monica Apr 17 '13 at 14:11
  • It is for a vector(as in math) class, implemented through a template. It's derived classes should have x, y, z, w and so on. I really cant explain it very well, English is not my first language :) – Hassedev Apr 17 '13 at 14:15
  • If you want math types, I strongly suggest that you use an already implemented library. Eigen is an EXCELENT example: http://eigen.tuxfamily.org/index.php?title=Main_Page – Ian Medeiros Apr 17 '13 at 14:19
  • Yes, I know. I actually would prefer GLM, as I'm working with OpenGL. It would just be nice to be able to add my own functions to the classes. I've tried deriving from the classes of GLM, but the classes created too many bugs. As it is, maybe I have to declare the functions outside of the classes. – Hassedev Apr 17 '13 at 14:24

3 Answers3

7

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.

Arne Mertz
  • 24,171
  • 3
  • 51
  • 90
  • Some day I have to create my own language, with aliases for variables ;) Or perhaps just a preprocessor for c++ for adding aliases. – Hassedev Apr 17 '13 at 14:28
  • Maybe you could write a plugin/extension for one of the existing open source compilers ;) But then again those aliases would only confuse your colleagues reading your sources. Call the child by it's name... – Arne Mertz Apr 17 '13 at 14:52
4

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.

Community
  • 1
  • 1
Ian Medeiros
  • 1,746
  • 9
  • 24
2

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).

John Zwinck
  • 239,568
  • 38
  • 324
  • 436