[英]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 CB C
\\ /\\ /
Dd
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)
,通过假定动态型的x
是T
,会跳过任何运行时检查并计算从知识的正确地址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. F
的D
子对象的布局将与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
子对象需要不平凡的计算:
F
), using the vtable of A
A
的vtable找出完整对象的地址(碰巧是F
型) D
base class subobject of f
, using the vtable again 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.