So all this time I thought that when you do something like ObjectA.field1, ObjectA is just like any value on the stack and you basically access its fields. Now I was going throu
Objects aren't really that magical. Essentially, an object just consists of a linear collection of all its members, with unspecified amounts of padding surrounding the members. Layout-wise, a C++ class is essentially like a C struct:
struct Foo {
int a;
char b;
std::string s;
static long q;
void bar() { print(s); log(a); }
static void car() { }
}
Ignoring member functions and statics for now, this might be laid out like this:
+= class Foo =+
+-------------+ ---\ <--- Foo * p
| int | s
+-------------+ i
| char | z
+-------------+ e
| <padding> | o
+-------------+ f
| std::string | (F
+-------------+ o
| <padding> | o)
+-------------+ ---/
Every object of class Foo
is stored like this in memory. The only extra data we need are the static members, member functions, and static member functions.
Static members are just global variables. So we have only one global variable:
+== static__Foo__q ==+
+--------------------+
| long int |
+--------------------+
Next up, static member functions are just ordinary, free functions:
void static__Foo__car() { }
Finally, member functions: these are essentially also just ordinary functions, though with an extra parameter that allows them to find instance members:
void member__Foo__bar(Foo * p) { print(p->s); log(p->a); }
The only important difference is that you cannot obtain an ordinary free function pointer to member functions, since the actual name of the implementation function is not exposed. The only way to refer to Foo::bar()
is via a pointer-to-member-function void (Foo::*ptfm)() = &Foo::bar
. Member objects are a bit simpler: you can either obtain a normal pointer to them, like Foo x; int * p = &x.a;
, but you can also form a pointer-to-member: int Foo::*ptm = &Foo::a;
.
Then, if we have objects Foo x, y, z;
, we can use the pairs of instance pointer Foo * pi = &x;
and member pointers int &Foo::* ptm = &Foo::a
or void (Foo::*ptfm)() = &Foo::bar
to access the relevant member of the given instance: the integer pi->*ptm
, and the function call (pi->*ptfm)()
, respectively. (Yes, ->*
is an operator.)
(A free version of the function pointer cannot exist, because polymorphic (virtual) functions require a more complicated dispatch mechanism than a simple, fixed function pointer.)
To get the field1
of some ObjectA
of some ClassA
the computer has to have the address of the memory zone containing ObjectA
, it knows (statically) the offset (in bytes) for field1
(of ClassA
) so it can retrieve the field1
by adding that offset to the adress of ObjectA
.