c++多重继承的理解

understanding of multiple inheritance for c++

我正在阅读multiple inheritance for c++ 论文中的示例:(第 377 页)

class A {virtual void f();};
class B {virtual void f(); virtual void g();};
class C: A, B {void f();};
A* pa = new C;
B* pb = new C;
C* pc = new C;
pa->f();
pb->f();
pc->f();
pc->g()

(1) Bjarne 写道:在进入 C::f 时,this 指针必须指向 C 对象的开头(而不是 B部分)。但是,在编译时通常不知道 pb 指向的 BC 的一部分,因此编译器无法减去常量 delta(B)。所以我们必须存储实际与 vtbl 一起存储的运行时的 delta(B)。所以 vtbl 条目现在看起来像:

struct vtbl_entry {
    void (*fct)();
    int  delta;
}

classc 的对象将如下所示:

----------             vtbl: 
   vptr -------------->-----------------------
   A part                C::f   | 0 
----------             -----------------------   
   vptr -------------->----------------------- 
   B part                C::f   | -delta(B)
----------               B::g   | 0
   C part              -----------------------
----------

Bjarne 写道:

pb->f() // call of C::f:
register vtbl_entry* vt = &pb->vtbl[index(f)];
(*vt->fct)((B*)((char*)pb+vt->delta)) //vt->delta is a negative number I guess

我在这里完全糊涂了。为什么 (B*) 不是 (*vt->fct)((B*)((char*)pb+vt->delta)) 中的 (C*)????根据我的理解和 Bjarne 在第 377 页 5.1 部分第一句的介绍,我们应该在这里传递一个 C* as this!!!!!!

紧接着上面的代码片段,Bjarne 继续写道: 请注意,对象指针可能必须调整为 po 在查找指向 vtbl 的成员之前,将其转换为正确的子对象。

哦,伙计!!!我完全不知道 Bjarne 想说什么?你能帮我解释一下吗?

一个C*,只是不是这样输入的。

坦率地说,这是一个非常糟糕的解释,而不是它是如何完成的。在 vtable 中存储函数指针更好更容易。

struct vtbl {
    void(*f)(B* b);
};
struct B {
   vtbl* vtable;
};
// Invoke function:
B* p = init();
p->vtable->f(p);
// Function pointer points to:
void f_thunk(B* b) {
    C* c = (char*)b - delta(B);
    C::f(c);
}

当编译器生成 thunk 时,它知道它们正在 thunk 的派生对象,因此它们不需要将偏移量存储在 vtable 中。编译器可以简单地偏移 thunk 中的指针,然后用指针委托给适当的方法。当然,这个 thunk 几乎只是生成的程序集,没有任何 C++ 表示,因此声明其中的指针具有任何特定的 C++ 类型是无效的。

你在这里的杂草丛中迷路了。

您要编写自己的 C++ 编译器吗?如果是这样,请随时忽略我。但是,如果您只是想理解和学习 C++ 虚拟继承(听起来就是这样),那么 none 您编写的内容非常重要。

只有编译器作者需要完全理解和弄清楚 vtbl 的工作原理,包括所有细节,才能真正实现 C++。不需要用 C++ 进行有效的编程和开发。从 class 的角度来看,所有需要理解的是虚拟继承是如何工作的。只要你明白调用 pb 的方法时你实际上最终会调用 C 的方法,以及为什么(为什么只是“因为它实际上是 C),这就是几乎所有需要了解的内容。

哦,你的 class 应该有一个虚拟析构函数,但那是另一回事了。

C++ 代码通常甚至无法访问 vtbl。而且 C++ 标准甚至不需要 C++ 编译器来实际实现任何所谓的 "vtable"。唯一的要求是虚拟继承和虚拟方法调用必须如何工作的规范。任何产生相同结果的实际实施都是合规的。

I'm totally confused here. Why (B*) not a (C*) in (*vt->fct)

在那个级别,唯一已知的类型是 B。实际对象可以是 CFooBar.

类型

但是,那篇论文有点过时了。现代编译器中的实际实现可能非常不同。 显示了如何在不将 delta(B) 添加到 vtable 的情况下完成此操作。