我正在阅读本文“虚方法表”
以上文章中的示例:
class B1 {
public:
void f0() {}
virtual void f1() {}
int int_in_b1;
};
class B2 {
public:
virtual void f2() {}
int int_in_b2;
};
class D : public B1, public B2 {
public:
void d() {}
void f2() {} // override B2::f2()
int int_in_d;
};
B2 *b2 = new B2();
D *d = new D();
在本文中,作者介绍了对象的内存布局d
是这样的:
d:
D* d--> +0: pointer to virtual method table of D (for B1)
+4: value of int_in_b1
B2* b2--> +8: pointer to virtual method table of D (for B2)
+12: value of int_in_b2
+16: value of int_in_d
Total size: 20 Bytes.
virtual method table of D (for B1):
+0: B1::f1() // B1::f1() is not overridden
virtual method table of D (for B2):
+0: D::f2() // B2::f2() is overridden by D::f2()
问题是关于d->f2()
。调用d->f2()
将B2
指针作为this
指针传递,因此我们必须执行以下操作:
(*(*(d[+8]/*pointer to virtual method table of D (for B2)*/)[0]))(d+8) /* Call d->f2() */
为什么我们要传递一个B2
指针作为this
指针而不是原始D
指针呢???我们实际上是在调用D :: f2()。根据我的理解,我们应该传递一个D
指向this
D :: f2()函数的指针。
___更新____
如果传递一个B2
指向this
D :: f2()的指针,如果我们要访问B1
D :: f2()中的类成员该怎么办?我相信B2
指针(this)如下所示:
d:
D* d--> +0: pointer to virtual method table of D (for B1)
+4: value of int_in_b1
B2* b2--> +8: pointer to virtual method table of D (for B2)
+12: value of int_in_b2
+16: value of int_in_d
它已经对该连续内存布局的起始地址具有一定的偏移量。例如,我们要访问b1
内部d :: F2(),我想在运行时,它会做这样的事情:*(this+4)
(this
指向同一地址B2),这将分b2
在B
????
我们不能将D
指针传递给虚拟函数覆盖B2::f2()
,因为同一虚拟函数的所有覆盖都必须接受相同的内存布局。
由于B2::f2()
函数期望B2
将对象的内存布局作为其this
指针传递给对象,即
b2:
+0: pointer to virtual method table of B2
+4: value of int_in_b2
覆盖功能也D::f2()
必须期望相同的布局。否则,功能将不再可互换。
要了解为什么互换性很重要,请考虑以下情形:
class B2 {
public:
void test() { f2(); }
virtual void f2() {}
int int_in_b2;
};
...
B2 b2;
b2.test(); // Scenario 1
D d;
d.test(); // Scenario 2
B2::test()
f2()
在这两种情况下都需要打电话。它没有更多的信息,告诉它如何this
指针有使这些电话时要调整*。这就是为什么编译器传递固定指针的原因,因此test()
对的调用f2
将与D::f2()
和一起使用B2::f2()
。
*其他实现可能很好地传递此信息;但是,本文中讨论的多重继承实现无法做到这一点。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句