当我们创建一个类的对象时,它的内存映射是什么样的.我对对象如何调用非虚拟成员函数更感兴趣.编译器是否创建了一个像vtable一样的表,它在所有对象之间共享?
class A
{
public:
void f0() {}
int int_in_b1;
};
A * a = new A;
Run Code Online (Sandbox Code Playgroud)
什么是内存地图?
小智 13
你可以想象这段代码:
struct A {
void f() {}
int int_in_b1;
};
int main() {
A a;
a.f();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
变成以下的东西:
struct A {
int int_in_b1;
};
void A__f(A* const this) {}
int main() {
A a;
A__f(&a);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
调用f是直截了当的,因为它是非虚拟的.(有时对于虚拟调用,如果已知对象的动态类型,则可以避免虚拟调度,因为它在此处.)
一个较长的例子,可以让您了解虚拟功能如何工作或让您感到非常困惑:
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;
}
Run Code Online (Sandbox Code Playgroud)
变成这样的东西:
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;
}
Run Code Online (Sandbox Code Playgroud)
每个对象只有一个vtable指针,您可以向该类添加许多虚拟方法,而不会影响对象大小.(vtable增长了,但每个类存储一次并且不是很大的开销.)请注意,我在这个例子中简化了许多细节,但它确实有效:析构函数没有被解决(这里应该另外是虚拟的),它会泄漏内存,而__func__值会略有不同(它们由编译器为当前函数的名称生成)等等.