当使用多重继承时,静态转换什么时候是安全的?
我发现自己处于一种知道某物是什么类型的情况。类型是三个(或更多)继承级别之一。我调用返回 B*
的工厂,但是 T 是类型的最高级别(如果我的代码知道它是什么)或第二级别。
不管怎样,我在模板中做了一个static_cast
,这是错误的做法。我的问题是我什么时候可以安全地进行静态投射?有这样的时候吗?我在这种情况下这样做是因为当我不小心将 T 作为一些古怪的东西(已经发生并且)动态转换忽略(并返回 null)时,我宁愿得到编译错误。然而,当我知道正确的类型时,指针不会被调整,导致我有一个错误的指针。我根本不知道为什么在这种情况下允许静态转换。
什么时候可以安全地使用 static_cast 进行向下转换?有没有出现过这样的情况?现在看来使用 static_cast
总是错误的(当目的是向下转换时)
好吧,我想出了如何重现它。
#include <iostream>
struct B { virtual void f1(){} };
struct D1 : B {int a;};
struct D2 : B {int a, b; };
struct DD : D1, D2 {};
int main(){
void* cptr = new DD(); //i pass it through a C interface :(
B* a = (B*)cptr;
D2* b = static_cast<D2*>(a); //incorrect ptr
D2* c = dynamic_cast<D2*>(a); //correct ptr
std::cout << a << " " <<b << " " <<c;
}
I found myself in a situation where I know what type something is. The Type is one of three (or more) levels of inheritance. I call factory which returns B*
however T is either the highest level of a type (if my code knows what it is) or the 2nd level.
Anyways, I did a static_cast
in the template which is the wrong thing to do. My question is WHEN can I static cast safely? Is there ever such a time? I did it in this case because I'd rather get compile errors when I accidentally have T as something wacky which (has happened and) dynamic cast ignores (and returns null). However when I know the correct type the pointer is not adjusted causing me to have a bad pointer. I'm not sure why static cast is allowed in this case at all.
When can I use static_cast for down casting safely? Is there ever a situation? Now it seems like it always is wrong to use a static_cast
(when the purpose is to down cast)
Ok I figured out how to reproduce it.
#include <iostream>
struct B { virtual void f1(){} };
struct D1 : B {int a;};
struct D2 : B {int a, b; };
struct DD : D1, D2 {};
int main(){
void* cptr = new DD(); //i pass it through a C interface :(
B* a = (B*)cptr;
D2* b = static_cast<D2*>(a); //incorrect ptr
D2* c = dynamic_cast<D2*>(a); //correct ptr
std::cout << a << " " <<b << " " <<c;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
交叉转换:
需要使用
dynamic_cast
,不能使用static_cast
来完成(static_cast
应该会导致编译时错误)。如果任一基类不是多态的(虚拟函数的存在不是可选的),dynamic_cast
也会失败。请参阅MSDN 上的此说明
A cross-cast:
requires use of
dynamic_cast
, it cannot be done withstatic_cast
(static_cast
should have caused a compile-time error).dynamic_cast
will also fail if either base class is not polymorphic (the presence of virtual functions is NOT optional).See this explanation on MSDN
如果
Derived
将Base
作为公共(或以其他方式可访问)基类,并且d
的类型为Derived*
,则static_cast (d)
是向上转换。这在技术上始终是安全的。
通常是不必要的,除非您隐藏(隐藏)方法的情况。
干杯&呵呵,
If
Derived
hasBase
as a public (or otherwise accessible) base class, andd
is of typeDerived*
, thenstatic_cast<Base*>(d)
is an upcast.This is always technically safe.
And generally unnecessary, except for cases where you have hiding (shadowing) of method.
Cheers & hth.,
问题在于这一行:
如果将某些内容转换为 void 指针,则必须在执行任何其他强制转换之前将其转换回最初转换的相同类型。如果遇到多种不同类型的对象必须通过同一个 void 指针的情况,那么您需要先将其转换为通用类型,然后再转换为 void 指针。
编辑:
如果您只知道 cptr 指向某个在转换时派生自 B 的类型的对象,那么这还不足以继续下去。当您尝试将 DD 指针转换为 B 指针时,编译器会让您知道这一点。
你必须做的是这样的:
但我不确定这在你的实际使用中是否可以接受。
The problem lies with this line:
If you convert something to a void pointer, you must convert it back to the same type that it was converted from first before doing any other casts. If you have a situation where multiple different types of objects have to go through the same void pointer, then you need to first cast it down to a common type before converting to a void pointer.
EDIT:
If you only know that cptr points to some object which is of a type derived from B at the time of the cast, then that isn't enough information to go on. The compiler lets you know that when you try to convert the DD pointer to a B pointer.
What you would have to do is something like this:
but I'm not sure if that is acceptable in your actual usage.
如果您确定该对象实际上是该类的实例,则可以安全地向上转换。
You can safely upcast if you are sure that the object is actually an instance of that class.
只是为了完整性(知道我迟到了一点,只是为了像我这样迟到的读者......):
如果使用正确,
static_cast
可以应用!首先,简单的情况:
您可以通过中间向下转换为
DD*
从D1*
到D2*
:向上转换为
那么 D2*
是隐式的。即使对于非虚拟继承也是可能的。但请注意,在进行向下转换时,您需要 100% 确保d1
确实被创建为DD
,否则最终会出现 undefined行为!现在更复杂的情况:钻石图案!这就是问题中提出的内容:
现在这个演员已经很危险了!这里实际实现的是reinterpret_cast:
您想要的是一个简单的向上转换。通常,根本不需要强制转换:
唯一的:
DD
有两个继承的B
实例。如果D1
和D2
都从B
继承虚拟,那么它就会起作用(struct D1/2 : virtual B { };
– 不要与作为虚拟类的B
/D1
/D2
相混淆!)。现在,对相应基
D1
或D2
的转换可以清楚地表明应指向B
的两个继承实例中的哪一个。现在,您可以通过再次向下转换为
DD
来取回相应的另一个实例;由于钻石图案,您需要再次进行中间铸造:关于所有这一切的非常重要的一点是:在向下铸造时,您绝对需要使用与向上铸造相同的钻石边缘!
结论:是的,使用静态强制转换是是可能的,并且如果所涉及的类不是虚拟的,那么它是唯一的选择(注意:与虚拟继承不同!)。但很容易无法正确执行此操作,有时甚至是不可能的(例如,如果在
std::vector
中存储了指向任意派生类型的基类型指针),因此通常,dynamic_cast
解决方案,如 Ben 是更更安全的一个(前提是虚拟数据类型可用;如果是这样,在前面提到的向量示例,它是唯一解决方案!)。Just for completeness (knowing that I'm late a little, just for late readers like me...):
static_cast
can be applied, if used correctly!At first, the simple case:
You can get from
D1*
toD2*
via intermediate downcast toDD*
:The upcast to
D2*
is implicit then. This is possible even for non-virtual inheritance. But be aware that you need to be 100% sure thatd1
really was created asDD
when doing the downcast, otherwise you end up in undefined behaviour!Now the more complex case: Diamond pattern! This is what is presented in the question:
Now this cast is already is dangerous! What actually is implemented here is a reinterpret_cast:
What you instead want is a simple upcast. Normally, one would not need a cast at all:
Solely:
DD
has two inherited instances ofB
. It would have worked if bothD1
andD2
inherited virtually fromB
(struct D1/2 : virtual B { };
– not to be confused withB
/D1
/D2
being virtual classes!).The cast to the respective bases
D1
orD2
now makes clear which of the two inherited instances ofB
shall be pointed to.You now can get back the respective other instance by down-casting to
DD
again; due to the diamond pattern, you need an intermediate cast again:The very important point about all this matter is: You absolutely need to use, when down-casting, the same diamond edge you used for up-casting!!!
Conclusion: Yes, it is possible using static casts, and it is the only option if the classes involved are not virtual (note: to be differed from virtual inheritance!). But it is just too easy to fail in doing it correctly, sometimes even impossible (e. g. if having stored pointers of base type to arbitrary derived types in a
std::vector
), so usually, thedynamic_cast
solution as presented by Ben is the much safer one (provided virtual data types are available; if so, in the fore-mentioned vector example, it is the only solution!).交叉转换根本不需要动态转换..
A cross cast doesn't need a dynamic_cast at all..