C++ 多重虚拟继承与 COM
网络上充斥着对可怕的钻石问题"的解释.StackOverflow 也是如此.我想我理解这一点,但我无法将这些知识转化为理解相似但不同的东西.
The net is overflowing with explanations of the "dreaded diamond problem". So is StackOverflow. I think I understand that bit, but I fail to translate that knowledge into comprehending something similar yet different.
我的问题从纯 C++ 问题开始,但答案很可能会扩展到 MS-COM 细节.一般问题的问题是:
My question begins as a pure C++ question, but the answer might well branch over into MS-COM specifics. The general problem question goes:
class Base { /* pure virtual stuff */ };
class Der1 : Base /* Non-virtual! */ { /* pure virtual stuff */ };
class Der2 : Base /* Non-virtual! */ { /* pure virtual stuff */ };
class Join : virtual Der1, virtual Der2 { /* implementation stuff */ };
class Join2 : Join { /* more implementation stuff + overides */ };
这不是经典的钻石解决方案.虚拟"在这里究竟做了什么?
This is not the classic diamond solution. Exactly what does "virtual" do here?
我真正的问题是试图理解 在 CodeProject 上我们朋友的讨论. 它涉及一个自定义类,用于为 Flash 播放器创建透明容器.
My real problem is trying to understand a discussion over at our friends' place at CodeProject. It involves a custom class for creating a transparent container for the Flash player.
我想我会试试这个地方玩.事实证明,使用 Flash 播放器 10 版的以下声明会使您的应用崩溃.
I thought I would try this place for fun. It turns out that the following declaration crashes your app, with version 10 of the Flash player.
class FlashContainerWnd: virtual public IOleClientSite,
virtual public IOleInPlaceSiteWindowless,
virtual public IOleInPlaceFrame,
virtual public IStorage
调试表明,当输入来自不同调用者的函数实现(QueryInterface 等)时,对于不同的调用,我会得到不同的this"指针值.但是删除虚拟"就可以了!没有崩溃,同样的this"指针.
Debugging shows that when entering the function implementations (QueryInterface etc), from different callers, I get different "this"-pointer values for different calls. But removing "virtual" does the trick! No crashes, and same "this"-pointer.
我想清楚地了解到底发生了什么.非常感谢.
I would like to clearly understand exactly what is going on. Thanks a lot.
干杯亚当
推荐答案
第一个示例中的虚拟继承没有做任何事情.我敢打赌,如果它们被删除,它们会编译成相同的代码.
The virtual inheritance in the first example don't do anything. I would wager that they compile to the same code if they were removed.
虚拟继承的类只是标记编译器,它应该合并以后版本的 Der1
或 Der2
.由于每一个都只出现在继承树中,因此没有做任何事情.虚拟对象对 Base
没有影响.
The virtually inherited class just flag the compiler that it should merge later versions of Der1
or Der2
. Since only one of each appears in the inheritance tree nothing is done. The virtuals have no effect on Base
.
auto p = new Join2;
static_cast<Base*>(static_cast<Der1*>(p)) !=
static_cast<Base*>(static_cast<Der2*>(p))
虚拟继承仅影响下一个继承的类,并且仅适用于已被定义为虚拟的实例.这与您的预期相反,但它是对类编译方式的限制.
The virtual inheritance only effects the next inherited class, and only for instances that have been delcared virtual. This is backward from what you would expect, but it's a limitation on the way classes are compiled.
class A {};
class B : virtual public A {};
class C : virtual public A {};
class D : public A {};
class E : virtual public A, public B, public C, public D {};
class F : public A, public B, public C, public D {};
F::A != F::B::A or F::C::A or F::D::A
F::B::A == F::C::A
F::D::A != F::B::A or F::C::A or F::A
E::B::A == E::C::A == E::A
E::D::A != E::B::A or E::C::A or E::D::A
必须在 C 和 B 中将 A 标记为 virtual 而不是 E 或 F 的原因之一是 C 和 B 需要知道不要调用 A 的构造函数.通常他们会初始化他们的每个副本.当他们参与钻石继承时,他们不会.但是你不能重新编译 B 和 C 以不构造 A.这意味着 C 和 B 必须提前知道在不调用 A 的构造函数的情况下创建构造函数代码.
One of the reasons A must be marked virtual in C and B instead of E or F is that C and B need to know not to call A's constructor. Normally they would have initialize each of their copies. When they are involved in diamond inheritance they wont. But you cant recompile B and C to not construct A. That means C and B have to know ahead of time to create constructor code where A's constructor is not called.
相关文章