我有三个班级:
class A {};
class B : virtual public A {};
class C : virtual public A {};
class D: public B, public C {};
尝试从A *到B *的静态强制转换我得到以下错误:
cannot convert from base A to derived type B via virtual base A
答案 0 :(得分:85)
为了理解演员系统,您需要深入了解对象模型。
简单层次结构模型的经典表示是包含:如果B
派生自A
,那么B
对象实际上将包含一个A
子对象属性。
使用此模型,向下转换是一种简单的指针操作,由编译时已知的偏移量决定,该偏移量取决于B
的内存布局。
这是 static_cast 的作用:静态强制转换被称为静态,因为在编译时计算演员必需的内容,无论是指针算术还是转换(*)。 / p>
然而,当virtual
继承踢的事情往往变得有点困难。主要问题是,使用virtual
继承,所有子类共享子对象的相同实例。为此,B
将指向A
,而不是A
,A
基类对象将在{B
之外实例化。 1}}。
因此,在编译时不可能推导出必要的指针算法:它取决于对象的运行时类型。
每当存在运行时类型依赖项时,您需要RTTI(运行时类型信息),并且使用RTTI进行强制转换是 dynamic_cast 的工作。
总结:
static_cast
dynamic_cast
另外两个也是编译时演员,但它们非常具体,很容易记住它们的用途......而且它们很臭,所以最好不要再使用它们。
(*)正如@curiousguy在评论中指出的那样,这仅适用于向下转换。无论虚拟或简单继承,static_cast
都允许向上转换,但演员也是不必要的。
答案 1 :(得分:12)
据我所知,您需要使用dynamic_cast
,因为继承是virtual
而您正在向下转发。
答案 2 :(得分:6)
在这种情况下你不能使用static_cast
因为编译器在编译时不知道B相对于A的偏移量。必须根据最派生对象的确切类型在运行时计算偏移量。因此,您必须使用dynamic_cast
。
答案 3 :(得分:4)
是的,您必须使用dynamic_cast,但您必须使基类A具有多态性,例如:通过添加虚拟dtor。
答案 4 :(得分:4)
根据标准文件,
5.2.9 - 9 ,静态演员,
“指向cv1 B的指针”类型的右值,其中B是类类型,可以转换为类型为“指向cv2的指针”的右值 D,“其中D是来自B的派生类(第10节),如果从”指向D的指针“到”指向B的指针“的有效标准转换 存在(4.10),cv2与cv1相同,或者cv资格比cv1更高,而 B既不是虚拟基类 D也不是D的虚拟基类的基类。
因此,您无法使用dynamic_cast
...
答案 5 :(得分:1)
$ 5.2.9 / 2-“表达式e可以 显式转换为类型T使用 表单的static_cast static_cast(e)如果声明 对于某些人来说,“T t(e);”格式正确 发明了临时变量t(8.5)。“
在您的代码中,您尝试使用'T = B *'和'e = A *'
进行static_cast现在'B * t(A *)'在C ++中的格式不正确(但'A * t(B *)'是因为'A'是'B'的虚拟明确且可访问的基础。因此,代码给出错误。
答案 6 :(得分:1)
我不知道这是否“安全”但是。
假设
B派生自A(和纯虚拟)
因为我知道指向B的指针仍然是指向B的指针。
class A
{
virtual void doSomething(const void* p) const =0;
};
class B
{
public:
int value;
virtual void doSomething(const void*p)const
{
const B * other = reinterpret_cast<const B*>(p);
cout<<"hello!"<< other->value <<endl;
}
};
int main()
{
B foo(1),bar(2);
A * p = &foo, q=&bar;
p->doSomething(q);
return 0;
}
这个程序执行并正确返回打印“你好!”和另一个对象的值(在这种情况下为“2”)。
顺便说一句,我正在做的事情是非常不安全的(我个人给每个类提供了一个不同的ID,并且在重新解释转换后断言当前ID等于其他ID以确保我们正在做两个相同类的事情)正如你所看到的那样,我将自己局限于“const”方法。因此,这将与“非const”方法一起使用,但如果你做错了什么,捕获bug几乎是不可能的。即使有断言,即使它应该失败,也有4亿次成功断言的机会 (断言(ID == other-&gt; ID);)顺便说一下..好的OO设计不应该要求这样的东西,但在我的情况下,我试图重构/重新设计代码而不能放弃重新解释铸造的用法。一般来说,你可以避免这种事情。