当我们创建一个类的对象时,它的内存映射是什么样的。我更感兴趣的是对象如何调用非虚拟成员函数。编译器是否创建一个像 vtable 这样在所有对象之间共享的表?
class A
{
public:
void f0() {}
int int_in_b1;
};
A * a = new A;
a 的内存映射是什么?
你可以想象这段代码:
struct A {
void f() {}
int int_in_b1;
};
int main() {
A a;
a.f();
return 0;
}
被转变为类似这样的东西:
struct A {
int int_in_b1;
};
void A__f(A* const this) {}
int main() {
A a;
A__f(&a);
return 0;
}
Calling 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;
}
变成类似这样的东西:
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;
}
每个对象只有一个vtable指针,你可以在类中添加很多虚方法而不影响对象大小。 (vtable 会增长,但是每个类都会存储一次,并且不会产生很大的大小开销。)请注意,我在这个示例中简化了许多细节,但它确实有效:析构函数没有被解决(这里应该另外是虚拟的),它会泄漏内存,并且__func__其中,值会略有不同(它们是由编译器为当前函数名称生成的)。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)