简体   繁体   English

虚拟多重继承和转换

[英]Virtual multiple inheritance and casting

I tried creating a class that inherits from multiple classes as followed, getting a "diamond" 我尝试创建一个继承自多个类的类,如下所示:
(D inherits from B and C. B and C both inherits from A virtually ): (D从B和C继承。B和C都实际上从A继承):

A 一种
/ \\ / \\
B C B C
\\ / \\ /
D d

Now, I have a container with a linked list that holds pointers to the base class ( A ). 现在,我有一个带有链接列表的容器,其中包含指向基类( A )的指针。
When I tried doing explicit casting to a pointer (after typeid check) I got the following error: 当我尝试对指针进行显式转换(在typeid检查之后)时,出现以下错误:
"cannot convert a pointer to base class "A" to pointer to derived class "D" -- base class is virtual" “无法将指向基类“ A”的指针转换为指向派生类“ D”的指针-基类是虚拟的”

But when I use dynamic casting it seems to work just fine. 但是,当我使用动态投射时,它似乎可以正常工作。
Can anyone please explain to me why I have to use dynamic casting and why does the virtual inheritance causes this error? 谁能向我解释为什么我必须使用动态强制转换,为什么虚拟继承会导致此错误?

"Virtual" always means "determined at runtime". “虚拟”始终表示“在运行时确定”。 A virtual function is located at runtime, and a virtual base is also located at runtime. 虚拟函数位于运行时,虚拟库也位于运行时。 The whole point of virtuality is that the actual target in question is not knowable statically. 虚拟性的全部要点是所讨论的实际目标不是静态可知的。

Therefore, it is impossible to determine the most-derived object of which you are given a virtual base at compile time, since the relationship between the base and the most-derived object is not fixed. 因此,无法确定在编译时为其提供虚拟基础的最派生对象,因为该基础与最派生对象之间的关系不固定。 You have to wait until you know what the actual object is before you can decide where it is in relation to the base. 您必须等到知道实际对象是什么之后,才能决定它相对于基础的位置。 That's what the dynamic cast is doing. 这就是动态转换的作用。

When I tried doing explicit casting to a pointer (after typeid check) 当我尝试对指针进行显式转换时(在typeid检查之后)

After a successful typeid(x) == typeid(T) you know the dynamic type of x , and you could in theory avoid any other runtime checking involved in dynamic_cast at that point. 成功的typeid(x) == typeid(T)您将知道x的动态类型,并且从理论上讲,您可以避免在此点涉及dynamic_cast任何其他运行时检查。 OTOH, the compiler is not required to do this kind of static analysis. OTOH,不需要编译器执行这种静态分析。

static_cast<T&>(x) does not convey to the compiler the knowledge that the dynamic type of x really is T : the precondition is weaker (that a T object has x as a subobject base class). static_cast<T&>(x)不传达给编译器的知识,动态型的x真的是T :前提是较弱(即一个T对象具有x作为子对象的基类)。

C++ could provide a static_exact_cast<T&>(x) which is only valid if x designates an object of dynamic type T (and not some type derived from T , unlike static_cast<T&> ). C ++可以提供static_exact_cast<T&>(x) ,只有在x指定动态类型为T的对象(而不是从T派生的某种类型,而不是static_cast<T&> )时,它才有效。 This hypothetical static_exact_cast<T&>(x) , by assuming the dynamic type of x is T , would skip any runtime check and compute the correct address from knowledge of T object layout: because in 该假设static_exact_cast<T&>(x) ,通过假定动态型的xT ,会跳过任何运行时检查并计算从知识的正确地址T对象布局:因为在

D d;
B &br = d;

no runtime offset computation is necessary, in static_exact_cast<D&>(br) the reverse adjustment would involve no runtime offset computation. 不需要运行时偏移量计算,在static_exact_cast<D&>(br) ,反向调整将不涉及运行时偏移量计算。

Given 特定

B &D_to_B (D &dr) {
  return dr;
}

a runtime offset computation is needed in D_to_B (except if whole program analysis shows that no class derived from D has different offset of base class A ); D_to_B需要运行时偏移量计算(除非整个程序分析表明没有派生自D类具有与基类A不同的偏移量); given 特定

struct E1 : virtual A
struct E2 : virtual A
struct F : E1, D, E2

the layout of the D subobject of F will be different from the layout of a D complete object: the A subobject will be at a different offset. FD子对象的布局将与D完整对象的布局不同: A子对象的偏移量将不同。 The offset needed by D_to_B will be given by the vtable of D (or stored directly in the object); D_to_B所需的偏移量将由D的vtable给出(或直接存储在对象中); it means that D_to_B will not just involve a constant offset as a simple "static" upcast (before entering the constructor of the object, the vptr is not set up so such casting cannot work; be careful with up casts in constructor init list). 这意味着D_to_B将不只是作为一个简单的“静态”向上D_to_B而涉及常量偏移(在进入对象的构造函数之前,未设置vptr,因此这种转换无法正常工作;在构造函数init列表中进行转换时要小心)。

And BTW, D_to_B (d) is not different from a static_cast<B&> (d) , so you see that the runtime computation of an offset can be done inside a static_cast . 顺便说一句, D_to_B (d)static_cast<B&> (d)并无不同,因此您可以看到可以在static_cast内部完成偏移量的运行时计算。

Consider the following code compile naively (assuming no fancy analysis showing that ar has dynamic type F ): 考虑以下代码天真地编译(假设没有花哨的分析表明ar具有动态类型F ):

F f;
D &dr = f; // static offset
A &ar = dr; // runtime offset
D &dr2 = dynamic_cast<D&>(ar);

Finding the A base class subject from a reference to D (a lvalue of unknown dynamic type) requires a runtime check of the vtable (or equivalent). 从对D的引用(动态类型未知的左值)的引用中找到A基类主题需要对vtable(或等效表)进行运行时检查。 Going back to the D subobject requires a non trivial computation: 返回到D子对象需要不平凡的计算:

  • finding out the address of the complete object (which happens to be of type F ), using the vtable of A 使用A的vtable找出完整对象的地址(碰巧是F型)
  • finding out the address of the unambiguous and publicly derived D base class subobject of f , using the vtable again 再次使用vtable找出f的明确且公共派生的D基类子对象的地址

This is not trivial as dynamic_cast<D&>(ar) statically knows nothing specific about F (the layout of F , the layout of the vtable of F ); 这不是微不足道dynamic_cast<D&>(ar)静态一无所知特定大约F (布局F ,的V表的布局F ); everything is fetched from the vtable. 一切都从vtable中获取。 All dynamic_cast knows is that there is a derived class of A and the vtable has all the information. dynamic_cast唯一知道的是,存在A的派生类,并且vtable具有所有信息。

Of course, there is no static_exact_cast<> in C++, so you have to use dynamic_cast , with the associated runtime checks; 当然,C ++中没有static_exact_cast<> ,因此您必须使用dynamic_cast以及相关的运行时检查; dynamic_cast is a complex function, but the complexity covers the base class cases; dynamic_cast是一个复杂的函数,但是复杂性涵盖了基类的情况。 when the dynamic type is given to dynamic_cast , the base classes tree walking is avoided, and the test is fairly simple. 当为dynamic_cast提供动态类型时,避免了基类的树行走,并且测试相当简单。

Conclusion: 结论:

Either you name the dynamic type in dynamic_cast<T> and dynamic_cast is fast and simple anyway, or you don't and the complexity of dynamic_cast is really needed. 您要么在dynamic_cast<T>命名动态类型,要么dynamic_cast无论如何都是快速而简单的,否则您就不会这样做了,确实需要dynamic_cast的复杂性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM