开发者

C++ class object memory map

开发者 https://www.devze.com 2022-12-22 09:33 出处:网络
When we create an object of a class what does it memory map look like. I am more interested in how the object calls the non virtual mem开发者_开发技巧ber functions. Does the compiler create a table li

When we create an object of a class what does it memory map look like. I am more interested in how the object calls the non virtual mem开发者_开发技巧ber functions. Does the compiler create a table like vtable which is shared between all objects?

class A
{
public:
  void f0() {}
  int int_in_b1;
};

A * a = new A;

What will be the memory map of a?


You can imagine this code:

struct A {
  void f() {}
  int int_in_b1;
};

int main() {
  A a;
  a.f();
  return 0;
}

Being transformed into something like:

struct A {
  int int_in_b1;
};
void A__f(A* const this) {}

int main() {
  A a;
  A__f(&a);
  return 0;
}

Calling f is straight-forward because it's non-virtual. (And sometimes for virtual calls, the virtual dispatch can be avoided if the dynamic type of the object is known, as it is here.)


A longer example that will either give you an idea about how virtual functions work or terribly confuse you:

struct B {
  virtual void foo() { puts(__func__); }
};
struct D : B {
  virtual void foo() { puts(__func__); }
};

int main() {
  B* a[] = { new B(), new D() };
  a[0]->foo();
  a[1]->foo();
  return 0;
}

Becomes something like:

void B_foo(void) { puts(__func__); }
void D_foo(void) { puts(__func__); }

struct B_VT {
  void (*foo)(void);
}
B_vtable = { B_foo },
D_vtable = { D_foo };

typedef struct B {
  struct B_VT* vt;
} B;
B* new_B(void) {
  B* p = malloc(sizeof(B));
  p->vt = &B_vtable;
  return p;
}

typedef struct D {
  struct B_VT* vt;
} D;
D* new_D(void) {
  D* p = malloc(sizeof(D));
  p->vt = &D_vtable;
  return p;
}

int main() {
  B* a[] = {new_B(), new_D()};
  a[0]->vt->foo();
  a[1]->vt->foo();
  return 0;
}

Each object only has one vtable pointer, and you can add many virtual methods to the class without affecting object size. (The vtable grows, but this is stored once per class and is not significant size overhead.) Note that I've simplified many details in this example, but it does work: destructors are not addressed (which should additionally be virtual here), it leaks memory, and the __func__ values will be slightly different (they're generated by the compiler for the current function's name), among others.


Recognize that the C++ language doesn't specify or mandate everything about memory layout for objects. That said, most compilers do it pretty much the same.

In your example, objects of type A require only enough memory to hold an int. Since it has no virtual functions, it needs no vtable. If the f0 member had been declared virtual, then objects of type A would typically start with a pointer to the class A vtable (shared by all objects of type A) followed by the int member.

In turn, the vtable has a pointer to each virtual function, defined, inherited or overridden. Calling a virtual function for an object consists of following the pointer to the vtable from the object, then using a fixed offset into the vtable (determined at compile time for each virtual function) to find the address of the function to call.


functions are not stored based on what class they are in.

usually the compiler will just treat any member function just like any other function except adds an argument for the 'this' pointer. which is automatically passed to the function when you called it based on the address of the object it is called on.

all functions, static, member, or even virtual member are stored in memory in the same way, they are all just functions.

when the compiler builds the code it pretty much hard codes where it goes into memory, then the linker goes through your code and replaces the "call the function with this name" command with "call the function at this hard coded address"


class A
{
public:
  void f0() {}
  void f1(int x) {int_in_b1 = x; }
  int int_in_b1;
};

A *a = new A();

is internally implemented (represented) like this: (function name are actually mangled)

struct A
{
  int int_in_b1;
};

void Class_A__constructor(struct a*) {} // default constructor
void Class_A__f0(struct a*) {}
void Class_A__f1(struct a*, int x) {a->int_in_b1 = x;}

// new is translated like this: (inline)
void* new() {
  void* addr = malloc(sizeof(struc a));
  Class_A__constructor(addr);
  return addr;
}

It can be verify by doing a command "nm" on the object file (result with mangled named)

0

精彩评论

暂无评论...
验证码 换一张
取 消