什么时候应该使用static_cast,dynamic_cast,const_cast和reinterpret_cast?
什么是正确的用途:
static_cast
-
dynamic_cast
-
const_cast
-
reinterpret_cast
- C风格演员
(type)value
- 函数式投射
type(value)
如何决定在哪些特定情况下使用哪个?
static_cast
是您应该尝试使用的第一个演员。 它执行types之间的隐式转换(例如int
到float
或void*
指针),还可以调用显式转换函数(或隐式转换函数)。 在很多情况下,明确声明static_cast
并不是必须的,但是注意到T(something)
语法相当于(T)something
并且应该避免(稍后再说)。 一个T(something, something_else)
是安全的,然而,保证调用构造函数。
static_cast
也可以通过inheritance层次结构进行投射。 向上(向基类)投射是不必要的,但是向下投射时,只要不通过virtual
inheritance投射就可以使用。 但是,它并没有检查,而且它是一个未定义的行为,将一个层次结构下的static_cast
转换为实际上不是该对象types的types。
const_cast
可以用来删除或添加const
到一个variables; 没有其他C ++强制转换能够删除它(甚至不reinterpret_cast
)。 需要注意的是,如果原始variables是const
,那么修改以前的const
值只是未定义的; 如果你使用它来把const
引用到没有用const
声明的东西,那么它是安全的。 例如,当基于const
重载成员函数时,这会很有用。 它也可以用来将const
添加到一个对象中,比如调用成员函数的重载。
const_cast
在volatile
上也是类似的,尽pipe这种情况不太常见。
dynamic_cast
几乎专门用于处理多态性。 您可以将任何多态types的指针或引用强制转换为任何其他类types(多态types至less具有一个声明或inheritance的虚函数)。 您可以使用它不仅仅是向下投掷 – 您可以侧身投掷,甚至可以投掷另一个链条。 dynamic_cast
将找出所需的对象,并在可能的情况下返回。 如果不能,则在指针的情况下它将返回nullptr
,或者在引用的情况下抛出std::bad_cast
。
dynamic_cast
有一些限制。 如果在inheritance层次结构中存在多个相同types的对象(所谓的“可怕钻石”),并且您没有使用virtual
inheritance,则不起作用。 它也只能通过公共inheritance – 它总是不能通过protected
或private
inheritance。 然而,这是很less有问题的,因为这种inheritanceforms很less见。
reinterpret_cast
是最危险的演员,应该非常谨慎地使用。 它将一种types直接转换为另一种types,比如将值从一个指针转换为另一个指针,或者将指针存储在一个int
,或者存储其他各种令人讨厌的东西。 在很大程度上,唯一保证你得到reinterpret_cast
是通常如果你把结果回到原来的types,你会得到完全相同的值(但不是如果中间types比原来的types小)。 reinterpret_cast
也有不less转换。 它主要用于奇怪的转换和位操作,如将原始数据stream转换为实际数据或将数据存储在alignment指针的低位中。
C风格转换和函数风格转换分别使用(type)object
或type(object)
。 C风格的转换被定义为以下的第一个成功:
-
const_cast
-
static_cast
(尽pipe忽略访问限制) -
static_cast
(见上),然后是const_cast
-
reinterpret_cast
-
reinterpret_cast
,然后是const_cast
因此,在某些情况下,它可以用来替代其他types的cast,但由于能够转化为reinterpret_cast
,所以可能会非常危险,而且当需要显式转换时,后者应该是首选的,除非您确定static_cast
会成功或reinterpret_cast
将失败。 即使如此,考虑更长,更明确的select。
C风格强制转换在执行static_cast
时也会忽略访问控制,这意味着他们有能力执行其他强制转换无法执行的操作。 不过,这主要是一个混乱,而在我看来,这只是避免C风格演员的另一个原因。
使用dynamic_cast
在inheritance层次结构中转换指针/引用。
使用static_cast
进行普通的types转换。
使用reinterpret_cast
进行位模式的低级重新解释。 谨慎使用。
使用const_cast
来转换const/volatile
。 避免这种情况,除非你使用常量不正确的API。
(上面已经给出了很多理论和概念上的解释)
以下是我使用static_cast , dynamic_cast , const_cast , reinterpret_cast时的一些实际示例 。
(也可以参考这个来理解说明: http : //www.cplusplus.com/doc/tutorial/typecasting/ )
static_cast:
OnEventData(void* pData) { ...... // pData is a void* pData, // EventData is a structure eg // 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); ..... }
dynamic_cast:
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( ... )*/{ // ... } }
const_cast:
// *Passwd declared as a const const unsigned char *Passwd // on some situation it require to remove its constness const_cast<unsigned char*>(Passwd)
reinterpret_cast:
typedef unsigned short uint16; // Read Bytes returns that 2 bytes got read. bool ByteBuffer::ReadUInt16(uint16& val) { return ReadBytes(reinterpret_cast<char*>(&val), 2); }
如果你知道一些内部信息可能会有所帮助
的static_cast
- C ++编译器已经知道如何将诸如float的缩放器types转换为int。 为他们使用static_cast。
- 通常,在将Atypes转换为Btypes时,static_cast会调用B的构造函数来传递它。如果B没有这样的构造函数,那么你会得到编译时错误。
- 如果A和B处于inheritance层次结构(或无效状态),则从
A*
到B*
总是成功,否则会出现编译错误。 - 问题 :如果你把基指针指向派生指针,但是如果它不是有效的转换,那么你不会得到错误。 你得到错误的指针,只要你尝试访问派生指针的成员,你会得到段错误。
-
A&
B&
。 - 陷阱 :从派生到基地或反之亦然创造新的副本! 对于来自C#/ Java的人来说,上面的许多都可能是一个巨大的惊喜。
的dynamic_cast
- dynamic_cast使用运行时types信息来判断cast是否有效。 例如,如果指针实际上不是派生types,则
(Base*)
到(Derived*)
可能会失败。 - 这意味着,dynamic_cast与static_cast相比非常昂贵!
- 对于
A*
到B*
,如果强制转换无效,则dynamic_cast将返回nullptr。 - 对于
A&
B&
如果强制转换无效,则dynamic_cast将抛出bad_castexception。
const_cast会
- 虽然static_cast可以做non-const来强制它不能以其他方式。 const_cast可以执行两种方法。
- 一个很好用的例子是遍历一些像
set<T>
容器,它只返回它的元素作为const,以确保你不会改变它的键。 但是,如果你的意图是修改对象的非关键成员,那么它应该没问题。 你可以使用const_cast去除常量。 - 另一个例子是当你想要实现
T& foo()
以及const T& foo()
。 为了避免代码重复,可以使用const_cast从另一个函数返回一个函数的值。 - 与上面两个强制转换不同的是,运行时开销很大。
reinterpret_cast的
- 这基本上说,把这些字节在这个内存位置,并认为它是给定的对象。
- 例如,可以将4个字节的float加载到int的4个字节中,以查看float中的位是怎样的。
- 显然,如果数据不正确的types,你可能会得到段错误。
- 这个强制转换没有运行时开销。
这是否回答你的问题?
我从来没有使用reinterpret_cast
,并怀疑是否遇到需要它的情况是不是一个坏devise的气味。 在代码库中,我在dynamic_cast
上的工作使用了很多。 与static_cast
的不同之处在于, dynamic_cast
执行运行时检查哪些可能(更安全),也可能不会(更多开销)是您想要的(请参阅msdn )。
除了到目前为止的其他答案,这里是不明显的例子,其中static_cast
是不够的,所以reinterpret_cast
是必要的。 假设有一个函数在输出参数中返回指向不同类的对象的指针(它们不共享一个基类)。 这个函数的一个真实的例子是CoCreateInstance()
(见最后一个参数,实际上是void**
)。 假设你从这个函数请求特定类的对象,所以你事先知道指针的types(你经常为COM对象所做的)。 在这种情况下,您不能使用static_cast
将指针指向void**
的指针void**
:您需要reinterpret_cast<void**>(&yourPointer)
。
在代码中:
#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) );
然而, static_cast
适用于简单的指针(不是指向指针的指针),所以上面的代码可以用下面的方法重写,以避免reinterpret_cast
(以一个额外的variables的价格):
#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);