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
指向的 B
是 C
的一部分,因此编译器无法减去常量 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
。实际对象可以是 C
、Foo
或 Bar
.
类型
但是,那篇论文有点过时了。现代编译器中的实际实现可能非常不同。 显示了如何在不将 delta(B)
添加到 vtable 的情况下完成此操作。
我正在阅读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
指向的 B
是 C
的一部分,因此编译器无法减去常量 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
。实际对象可以是 C
、Foo
或 Bar
.
但是,那篇论文有点过时了。现代编译器中的实际实现可能非常不同。 delta(B)
添加到 vtable 的情况下完成此操作。