When should static_cast, dynamic_cast, const_cast and reinterpret_cast be used?
以下各项的正确用途是什么:
static_cast dynamic_cast const_cast reinterpret_cast - C型铸造
(type)value 。 - 功能型铸造
type(value) 。
在特定的情况下,如何决定使用哪种方法?
EDCOX1〔11〕可用于删除或添加EDCOX1·12的变量,不存在其他C++的CAST能够移除它(甚至EDCOX1×13)。需要注意的是,只有当原始变量为
不过,
C样式转换和函数样式转换分别使用
const_cast static_cast (尽管忽略了访问限制)static_cast (见上文),然后是const_cast 。reinterpret_cast reinterpret_cast ,然后是const_cast 。
因此,在某些情况下,它可以用作其他类型的强制转换的替代品,但由于能够转换为
C样式的强制转换在执行
使用
普通类型转换使用
使用
使用
(上面有很多理论和概念上的解释)
下面是我使用静态、动态、const、reinterpret时的一些实际示例。
(另请参考此部分了解解释:http://www.cplusplus.com/doc/tutorial/typecasting/)
静态铸造:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | OnEventData(void* pData) { ...... // pData is a void* pData, // EventData is a structure e.g. // typedef struct _EventData { // std::string id; // std:: string remote_id; // } EventData; // On Some Situation a void pointer *pData // has been static_casted as // EventData* pointer EventData *evtdata = static_cast<EventData*>(pData); ..... } |
动态铸造:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | void DebugLog::OnMessage(Message *msg) { static DebugMsgData *debug; static XYZMsgData *xyz; if(debug = dynamic_cast<DebugMsgData*>(msg->pdata)){ // debug message } else if(xyz = dynamic_cast<XYZMsgData*>(msg->pdata)){ // xyz message } else/* if( ... )*/{ // ... } } |
康斯坦斯卡斯特:
1 2 3 4 5 6 7 8 | // *Passwd declared as a const const unsigned char *Passwd // on some situation it require to remove its constness const_cast<unsigned char*>(Passwd) |
重新解释铸造:
1 2 3 4 5 6 7 | typedef unsigned short uint16; // Read Bytes returns that 2 bytes got read. bool ByteBuffer::ReadUInt16(uint16& val) { return ReadBytes(reinterpret_cast<char*>(&val), 2); } |
如果你知道一点内在的东西可能会有所帮助…
静态铸造
- C++编译器已经知道如何在标量类型(例如浮点到int)之间进行转换。
- 当要求编译器从类型
A 转换为B 时,static_cast 调用B 的构造函数,将A 作为参数传递。或者,A 可以有一个转换运算符(即A::operator B() )。如果B 没有这样的构造函数,或者A 没有转换运算符,则会得到编译时错误。 - 从
A* 到B* 的强制转换总是成功的,如果a和b处于继承层次(或无效),否则会出现编译错误。 - gotcha:如果您将基指针强制转换为派生指针,但如果实际对象不是真正的派生类型,则不会得到错误。你得到了坏的指针,很可能在运行时出现一个segfault。同样适用于
A& 至B& 。 - gotcha:从派生到基的强制转换或viceversa创建新副本!对于来自C/Y/Java的人来说,这可能是一个巨大的惊喜,因为结果基本上是从派生出来的切掉的对象。
动态铸件
- 动态强制转换使用运行时类型信息来确定强制转换是否有效。例如,如果指针实际上不是派生类型,则指向
(Derived*) 的(Base*) 可能会失败。 - 这意味着,与静态铸造相比,动态铸造非常昂贵!
- 对于
A* 到B* ,如果强制转换无效,那么动态强制转换将返回nullptr。 - 对于
A& 到B& ,如果强制转换无效,那么动态强制转换将抛出错误的强制转换异常。 - 与其他类型转换不同,存在运行时开销。
康斯特卡斯特
- 虽然静态类型转换可以对常量执行非常量转换,但它不能向其他方向转换。警察演员可以同时扮演两种角色。
- 其中一个很方便的例子是迭代一些容器,比如
set ,它只返回常量元素,以确保您不更改其键。但是,如果您的目的是修改对象的非关键成员,那么它应该是正常的。你可以使用const-cast来移除const。 - 另一个例子是当您想要实现
T& foo() 和const T& foo() 时。为了避免代码重复,可以应用const-cast从另一个函数返回一个函数的值。
重新解释铸模
- 这基本上意味着在这个内存位置取这些字节,并将其视为给定的对象。
- 例如,您可以将4字节的float加载到4字节的int,以查看float中的位是什么样子的。
- 显然,如果数据对于类型不正确,您可能会得到segfault。
- 此转换没有运行时开销。
除其他答案外,这里还有一个不明显的例子,即
在代码中:
1 2 3 4 5 6 7 8 | #include <windows.h> #include <netfw.h> ..... INetFwPolicy2* pNetFwPolicy2 = nullptr; HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr, CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2), //static_cast<void**>(&pNetFwPolicy2) would give a compile error reinterpret_cast<void**>(&pNetFwPolicy2) ); |
但是,
1 2 3 4 5 6 7 8 9 | #include <windows.h> #include <netfw.h> ..... INetFwPolicy2* pNetFwPolicy2 = nullptr; void* tmp = nullptr; HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr, CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2), &tmp ); pNetFwPolicy2 = static_cast<INetFwPolicy2*>(tmp); |
这能回答你的问题吗?
我从来没有用过
虽然其他答案很好地描述了C++之间的所有差异,但我想添加一个简短的注释,说明为什么不应该使用C样式的EDCX1 0和EDCX1 1。
对于C++初学者来说,C风格的造型看起来像是C++上的超集操作(StasySkyCase<>)、DyrimixCase<>()、CistasyCase<>()、RealTytCase<>()),有人可以在C++的C++上更喜欢它们。实际上,C-StyleCast是一个超集,写起来更短。
C样式的强制转换的主要问题是隐藏了强制转换的开发者真实意图。C样式的强制转换可以执行几乎所有类型的强制转换,从静态强制转换<>()和动态强制转换<>()到潜在危险的强制转换,如const强制转换<>(),其中const修饰符可以删除,因此const变量可以修改和重新解释,甚至可以将整数值重新解释为指针。
这是样品。
1 2 3 4 5 6 7 8 9 10 | int a=rand(); // Random number. int* pa1=reinterpret_cast<int*>(a); // OK. Here developer clearly expressed he wanted to do this potentially dangerous operation. int* pa2=static_cast<int*>(a); // Compiler error. int* pa3=dynamic_cast<int*>(a); // Compiler error. int* pa4=(int*) a; // OK. C-style cast can do such cast. The question is if it was intentional or developer just did some typo. *pa4=5; // Program crashes. |
C++语言被添加到语言中的主要原因是允许开发者阐明他的意图——他为什么要去做那个演员。通过使用C++中完全有效的C样式转换,您的代码变得不可读,更容易出错,尤其是对于没有创建代码的其他开发人员来说。因此,为了使代码更加可读和显式,你应该总是喜欢C++风格的C型转换。
这是Bjarne Stroustrup(C++作者)的一本简短的报价,书是C++编程语言第四版-第302页。
This C-style cast is far more dangerous than the named conversion operators
because the notation is harder to spot in a large program and the kind of conversion intended by the programmer is not explicit.
为了理解,我们考虑下面的代码片段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | struct Foo{}; struct Bar{}; int main(int argc, char** argv) { Foo* f = new Foo; Bar* b1 = f; // (1) Bar* b2 = static_cast<Bar*>(f); // (2) Bar* b3 = dynamic_cast<Bar*>(f); // (3) Bar* b4 = reinterpret_cast<Bar*>(f); // (4) Bar* b5 = const_cast<Bar*>(f); // (5) return 0; } |
只有行(4)编译时没有错误。只有reinterpret_cast可用于将指向对象的指针转换为指向任何无关对象类型的指针。
需要注意的一个问题是:动态资源类型转换在运行时会失败,但是在大多数编译器上,它也会失败编译,因为在被转换的指针的结构中没有虚拟函数,这意味着动态资源类型转换只能使用多态类指针。
何时使用C++ CAST:
- 使用static_cast作为进行值转换的C样式转换的等价物,或者当我们需要显式地向上转换从类到其超类的指针时。
- 使用const-cast删除const限定符。
- 使用reinterpret_cast对整数和其他指针类型的指针类型进行不安全的转换。只有当我们知道我们在做什么,并且我们了解别名问题时,才使用这个方法。