为什么我们不能使用“虚拟继承"?在 COM 中? [英] Why can't we use "virtual inheritance" in COM?

查看:15
本文介绍了为什么我们不能使用“虚拟继承"?在 COM 中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我读过一些模糊的声明,虚拟继承没有't 提供了 COM 所需的内存结构,所以我们必须使用正常的继承.发明虚拟继承是为了处理钻石问题.

I have read some vague statement that virtual inheritance doesn't provide the memory structure required by COM, so we have to use the normal inheritance. Virtual inheritance is invented to handle the diamond problem.

谁能告诉我这两种继承方法之间内存结构细节的差异?以及虚拟继承不适合 COM 的关键原因.最好有图片.

Could someone show me an illustration of the difference of memory structure details between this two inherit approaches? And the key reason why virtual inheritance is not suitable for COM. A picture would be best.

非常感谢.

推荐答案

COM 接口在某种程度上很像 JAVA 接口——它们没有数据成员.这意味着当使用多重继承时,接口继承与类继承是不同的.

COM interfaces are rather like JAVA interfaces in a way - they don't have data members. This means that interface inheritance is different to class inheritance when multiple inheritance is used.

首先,考虑使用菱形继承模式的非虚拟继承...

To start with, consider non-virtual inheritance with diamond-shaped inheritance patterns...

  • B 继承 A
  • C 继承 A
  • D 继承 B 和 C

D 的一个实例包含 A 的数据成员的两个独立实例.这意味着当一个指向 A 的指针指向 D 的一个实例时,它需要识别它所指的 D 中的哪个 A 实例——指针在每种情况下都不同,并且指针转换不是类型的简单重新标记 - 地址也会改变.

An instance of D contains two separate instances of the data members of A. That means that when a pointer-to-A points into an instance of D, it needs to identify which instance of A within D it means - the pointer is different in each case, and pointer casts are not simple relabellings of the type - the address changes too.

现在考虑具有虚拟继承的同一个菱形.B、C 和 D 的实例都包含 A 的单个实例.如果您认为 B 和 C 具有固定布局(包括 A 实例),这是一个问题.如果 Bs 布局是 [A, x] 而 Cs 布局是 [A, y],那么 [B, C, z] 对 D 无效 - 它会包含 A 的两个实例.你必须使用类似于 [A, B', C', z] 其中 B' 是 B 中的所有内容,除了继承的 A 等.

Now consider the same diamond with virtual inheritance. Instances of B, C and D all contain a single instance of A. If you think of B and C having a fixed layout (including the A instance) this is a problem. If Bs layout is [A, x] and Cs layout is [A, y], then [B, C, z] is not valid for D - it would contain two instances of A. What you have to use is something like [A, B', C', z] where B' is everything from B except the inherited A etc.

这意味着如果你有一个指向 B 的指针,你就没有一个单一的方案来解除从 A 继承的成员的引用.根据指针是指向纯 B 还是指向纯 B 来查找这些成员是不同的B-within-D 或 B-within-something-else.编译器需要一些运行时线索(虚拟表)来查找从 A 继承的成员.你最终需要几个指向 D 实例中多个虚拟表的指针,因为继承的 B 和继承的 C 等都有一个 vtable,这意味着一些内存开销.

This means that if you have a pointer-to-B, you don't have a single scheme for dereferencing the members inherited from A. Finding those members is different depending on whether the pointer points to a pure-B or a B-within-D or a B-within-something-else. The compiler needs some run-time clue (virtual tables) to find the inherited-from-A members. You end up needing several pointers to several virtual tables in the D instance, as theres a vtable for the inherited B and for the inherited C etc, implying some memory overhead.

单继承不存在这些问题.实例的内存布局保持简单,虚拟表也更简单.这就是 Java 不允许类的多重继承的原因.在接口继承中没有数据成员,所以这些问题根本不会出现 - 没有 which-inherited-A-with-D 的问题,也没有根据特定 B 找到 A-within-B 的不同方法的问题恰好在里面.COM 和 Java 都可以允许接口的多重继承,而不必处理这些复杂问题.

Single inheritance doesn't have these issues. Memory layout of instances is kept simple, and virtual tables are simpler too. That's why Java disallows multiple inheritance for classes. In interface inheritance there are no data members, so again these problems simply don't arise - there's no issue of which-inherited-A-with-D, nor of different ways to find A-within-B depending on what that particular B happens to be within. Both COM and Java can allow multiple inheritance of interfaces without having to handle these complications.

编辑

我忘了说 - 没有数据成员,虚拟和非虚拟继承之间没有真正的区别.但是,对于 Visual C++,即使没有数据成员,布局也可能有所不同——无论是否存在任何数据成员,对每种继承样式都始终使用相同的规则.

I forgot to say - without data members, there is no real difference between virtual and non-virtual inheritance. However, with Visual C++, the layout is probably different even if there are no data members - using the same rules for each inheritance style consistently irrespective of whether any data members are present or not.

此外,COM 内存布局与 Visual-C++ 布局(对于支持的继承类型)相匹配,因为它就是为此而设计的.没有理由不能将 COM 设计为支持具有数据成员的接口"的多重和虚拟继承.Microsoft 本可以将 COM 设计为支持与 C++ 相同的继承模型,但选择不这样做 - 而且他们没有理由不这样做.

Also, the COM memory-layout matches the Visual-C++ layout (for supported inheritance types) because it was designed to do that. There's no reason why COM couldn't have been designed to support multiple and virtual inheritance of "interfaces" with data members. Microsoft could have designed COM to support the same inheritance model as C++, but chose not to - and there's no reason why they should have done otherwise.

早期的 COM 代码通常是用 C 编写的,这意味着必须与 Visual-C++ 布局精确匹配才能工作的手写结构布局.多重和虚拟继承的布局——好吧,我不会自愿手动做.此外,COM 始终是它自己的东西,旨在链接用多种不同语言编写的代码.它从未打算与 C++ 绑定.

Early COM code was often written in C, meaning hand-written struct layouts that had to precisely match the Visual-C++ layout to work. Layouts for multiple and virtual inheritance - well, I wouldn't volunteer to do it manually. Besides, COM was always its own thing, meant to link code written in many different languages. It was never intended to be tied to C++.

还有更多编辑

我意识到我错过了一个关键点.

I realised I missed a key point.

在 COM 中,唯一重要的布局问题是虚拟表,它只需要处理方法分派.布局上有显着差异,具体取决于您采用的是虚拟还是非虚拟方法,类似于 on object with data members 的布局...

In COM, the only layout issue that matters is the virtual table, which only has to handle method dispatch. There are significant differences in layout depending on whether you take the virtual or non-virtual approach, similar to the layout of on object with data members...

  • 对于非虚拟,D vtab 包含 A-within-B vtab 和 A-within-C vtab.
  • 对于虚拟,A 在 Ds vtable 中只出现一次,但对象包含多个 vtable,并且指针转换需要更改地址.

对于接口继承,这基本上是实现细节 - A 只有一组方法实现.

With interface-inheritance, this is basically implementation detail - there's only one set of method implementations for A.

在非虚拟情况下,A 虚拟表的两个副本将是相同的(导致相同的方法实现).它是一个稍大的虚拟表,但每个对象的开销更少,并且指针转换只是类型重新标记(没有地址更改).它的实现更简单、更高效.

In the non-virtual case, the two copies of the A virtual table would be identical (leading to the same method implementations). Its a slightly larger virtual table, but the per-object overhead is less and the pointer casts are just type-relabelling (no address change). It's simpler and more efficient implementation.

COM 无法检测到虚拟案例,因为对象或 vtable 中没有指示符.此外,当没有数据成员时,支持这两种约定是没有意义的.它只支持一种简单的约定.

COM can't detect the virtual case because there's no indicator in the object or vtable. Also, there's no point supporting both conventions when there's no data members. It just supports the one simple convention.

这篇关于为什么我们不能使用“虚拟继承"?在 COM 中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆