我有三个班级:
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
我有三个班级:
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
为了了解演员系统,您需要深入研究对象模型。
简单层次模型的经典表示是包含:如果从该对象B
派生,A
则该B
对象实际上将包含一个A
子对象及其自身属性。
使用此模型,向下转换是通过编译时已知的偏移量进行的简单指针操作,这取决于B
.
这就是static_cast所做的:静态转换被称为静态,因为转换所需的计算是在编译时完成的,无论是指针算术还是转换 (*)。
然而,当virtual
继承开始时,事情往往会变得更加困难。主要问题是通过virtual
继承,所有子类共享子对象的相同实例。为了做到这一点,B
将有一个指向 的指针A
,而不是一个A
正确的,并且A
基类对象将在B
.
因此,在编译时不可能推导出必要的指针算法:它取决于对象的运行时类型。
每当存在运行时类型依赖时,您都需要 RTTI(运行时类型信息),而使用 RTTI 进行强制转换是dynamic_cast的工作。
总之:
static_cast
dynamic_cast
另外两个也是编译时强制转换,但它们非常具体,很容易记住它们的用途......而且它们很臭,所以最好不要使用它们。
(*)正如@curiousguy 在评论中所指出的,这仅适用于向下转换。Astatic_cast
允许向上转换,而不管虚拟继承还是简单继承,尽管如此转换也是不必要的。
据我所知,您需要使用dynamic_cast
,因为继承是virtual
并且您正在向下转换。
您不能static_cast
在这种情况下使用,因为编译器在编译时不知道 B 相对于 A 的偏移量。偏移量必须在运行时根据最派生对象的确切类型计算。因此,您必须使用dynamic_cast
.
是的,您必须使用 dynamic_cast,但您必须使基类 A 具有多态性,例如通过添加虚拟 dtor。
根据标准文档,
第5.2.9 - 9节,对于静态投射,
“指向 cv1 B 的指针”类型的右值,其中 B 是类类型,可以转换为“指向 cv2 D 的指针”类型的右值,其中 D 是从 B 派生的类(第 10 条),如果一个有效的标准存在从“指向 D 的指针”到“指向 B 的指针”的转换 (4.10),cv2 与 cv1 具有相同的 cv 限定或大于 cv1 的 cv 限定,并且B 既不是 D 的虚拟基类也不是基类D的虚拟基类。
因此,这是不可能的,您应该使用dynamic_cast
...
$5.2.9/2-“如果声明“T t(e);”,则表达式 e 可以使用 static_cast(e) 形式的 static_cast 显式转换为类型 T;对于一些发明的临时变量 t (8.5) 是良构的。”
在您的代码中,您正在尝试使用 'T = B*' 和 'e = A*' 进行 static_cast
现在 'B* t(A*)' 在 C++ 中的格式不正确(但 'A* t(B*)' 是因为 'A' 是 'B' 的虚拟明确且可访问的基础。因此代码给出错误.
我不知道这是否“安全”,但是。
假设
B 派生自 A(和 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 以确保我们正在用 2 个相等的类做某事后断言)并且作为你看我把自己限制在“const”方法上。因此,这将适用于“非常量”方法,但如果你做错了什么,捕捉错误几乎是不可能的。即使有断言,即使它应该失败,也有 40 亿次中的 1 次成功断言的机会 (assert(ID== other->ID);)
顺便说一句.. 一个好的 OO 设计不应该需要这种东西,但在我的情况下,我尝试重构/重新设计代码而不能放弃重新解释转换的使用。一般来说,你可以避免这种事情。