era*_*ran 40 c++ virtual-inheritance downcast static-cast
请考虑以下代码:
struct Base {};
struct Derived : public virtual Base {};
void f()
{
Base* b = new Derived;
Derived* d = static_cast<Derived*>(b);
}
Run Code Online (Sandbox Code Playgroud)
这是标准([n3290: 5.2.9/2])禁止的,因此代码无法编译,因为Derived 实际上是继承自的Base.virtual从继承中删除使代码有效.
这条规则存在的技术原因是什么?
Ste*_*sop 36
技术问题是没有办法解决从子对象的开始和对象的开始Base*之间的偏移.BaseDerived
在你的例子中,它看起来没问题,因为只有一个类可以看到Base基数,因此继承是虚拟的,这似乎无关紧要.但是编译器不知道是否有人定义了另一个class Derived2 : public virtual Base, public Derived {},并且正在Base*指向它的Base子对象.通常[*],Base子对象和Derived子对象之间的偏移量Derived2可能与Base子对象和Derived最大派生类型的对象的完整对象之间的偏移量不同Derived,正是因为Base它实际上是继承的.
因此,根据动态类型的不同,无法知道完整对象的动态类型,以及给定转换的指针和所需结果之间的不同偏移量.因此演员阵容是不可能的.
你Base没有虚函数,因此没有RTTI,所以当然没有办法告诉完整对象的类型.即使Base有RTTI(我不会立即知道原因),演员仍然被禁止,但我想如果没有检查dynamic_cast在这种情况下是否可行.
[*]我的意思是,如果这个例子不能证明这一点,那么继续添加更多的虚拟继承,直到找到偏移量不同的情况;-)
从根本上来说,没有真正的原因,但目的是
static_cast非常便宜,最多涉及到指针的常量加法或减法。而且没有办法以如此低廉的成本实现你想要的演员阵容;Derived基本上,因为如果有额外的继承,对象中和的相对位置Base可能会改变,所以转换将需要大量dynamic_cast;的开销。委员会成员可能认为这违背了使用static_cast
而不是的理由dynamic_cast。
static_cast只能执行在编译时知道类之间的内存布局的那些强制转换。dynamic_cast可以在运行时检查信息,从而可以更准确地检查转换的正确性,以及读取有关内存布局的运行时信息。
虚拟继承将运行时信息放入每个对象中,这些信息指定了Base和之间的内存布局Derived。是一个接一个的还是另外一个缺口?由于static_cast无法访问此类信息,因此编译器将采取保守措施,并仅给出编译器错误。
更详细地:
考虑一个复杂的继承结构,其中-由于多重继承-存在多个副本Base。最典型的情况是钻石继承:
class Base {...};
class Left : public Base {...};
class Right : public Base {...};
class Bottom : public Left, public Right {...};
Run Code Online (Sandbox Code Playgroud)
在这种情况下,Bottom包含Left和Right,其中每个都有自己的副本Base。上述所有类的存储器结构在编译时都是已知的,static_cast可以毫无问题地使用。
现在让我们考虑类似的结构,但具有以下事实的虚拟继承Base:
class Base {...};
class Left : public virtual Base {...};
class Right : public virtual Base {...};
class Bottom : public Left, public Right {...};
Run Code Online (Sandbox Code Playgroud)
使用虚拟继承确保当Bottom被创建,它仅包含一个拷贝Base被共享对象部分之间Left和Right。Bottom对象的布局可以是例如:
Base part
Left part
Right part
Bottom part
Run Code Online (Sandbox Code Playgroud)
现在,考虑您强制转换Bottom为Right(这是有效的强制转换)。您将获得一个Right指向对象的指针,该指针分为两部分:Base并且Right在两者之间有一个内存间隙,其中包含(现在不相关)Left部分。有关该间隙的信息在运行时存储在Right(通常称为vbase_offset)的隐藏字段中。您可以在此处阅读详细信息。
但是,如果仅创建一个独立的Right对象,则不会存在差距。
因此,如果我只是给您一个指向您的指针,Right则在编译时不知道它是独立对象还是更大对象(例如Bottom)的一部分。您需要检查运行时信息以正确地从转换Right为Base。这就是为什么static_cast会失败而dynamic_cast不会失败的原因。
关于dynamic_cast的注意事项:
虽然static_cast不使用有关对象的运行时信息,但dynamic_cast使用并要求它存在!因此,后者的强制转换只能用于包含至少一个虚拟函数(例如,虚拟析构函数)的类上
| 归档时间: |
|
| 查看次数: |
15291 次 |
| 最近记录: |