有什么用途:
static_cast
dynamic_cast
const_cast
reinterpret_cast
C风格演员 (type)value
功能式演员 type(value)
如何决定在哪些特定情况下使用哪个?
static_cast
是你应该尝试使用的第一个演员.它执行诸如类型之间的隐式转换(例如int
to float
或指向void*
)之类的操作,并且它还可以调用显式转换函数(或隐式转换函数).在许多情况下,明确说明static_cast
不是必要的,但重要的是要注意T(something)
语法是等同于(T)something
并且应该避免(稍后将详细介绍).T(something, something_else)
但是,A 是安全的,并保证调用构造函数.
static_cast
也可以通过继承层次结构进行转换.向上向上(向基类)进行向上投射是不必要的,但是当向下向下投射时,只要它不通过virtual
继承进行强制转换就可以使用它.但是,它不进行检查,并且将static_cast
层次结构向下移动到实际上不是对象类型的类型是未定义的行为.
const_cast
可用于删除或添加const
变量; 没有其他C++演员能够删除它(甚至没有reinterpret_cast
).重要的是要注意,const
如果原始变量是const
,则仅修改以前的值是不确定的; 如果你使用它来取消对const
未声明的东西的引用const
,那么它是安全的.const
例如,当基于成员函数重载时,这可能很有用.它还可以用于添加const
到对象,例如调用成员函数重载.
const_cast
volatile
虽然这种情况不太常见,但也有类似的作用.
dynamic_cast
专门用于处理多态性.您可以将指向任何多态类型的指针或引用转换为任何其他类类型(多态类型至少具有一个虚函数,声明或继承).你可以使用它而不仅仅是向下投射 - 你可以侧身或甚至向上投射另一个链.该dynamic_cast
会寻求所需的对象,如果可能的话返回.如果不能,则nullptr
在指针的情况下返回,或者std::bad_cast
在引用的情况下抛出.
dynamic_cast
但是有一些限制.如果继承层次结构中存在多个相同类型的对象(所谓的"可怕的菱形")并且您没有使用virtual
继承,则它不起作用.它也只能通过公共继承 - 它总是无法通过protected
或private
继承.然而,这很少是一个问题,因为这种形式的遗传很少见.
reinterpret_cast
是最危险的演员,应该非常谨慎地使用.它将一种类型直接转换为另一种类型 - 例如将值从一个指针转换为另一个指针,或将指针存储在一个int
或各种其他讨厌的东西中.很大程度上,唯一的保证reinterpret_cast
是,通常如果将结果转换回原始类型,您将得到完全相同的值(但如果中间类型小于原始类型则不会).有许多转换reinterpret_cast
也无法做到.它主要用于特别奇怪的转换和位操作,例如将原始数据流转换为实际数据,或将数据存储在对齐指针的低位中.
C风格的演员表和功能风格的演员表分别是使用(type)object
或type(object)
类似的演员表,并且功能相同.它们被定义为以下第一个成功:
const_cast
static_cast
(虽然忽略了访问限制)
static_cast
(见上文),然后 const_cast
reinterpret_cast
reinterpret_cast
, 然后 const_cast
因此,它可以在某些情况下用作其他演员阵容的替代品,但由于能够转换为a而非常危险reinterpret_cast
,而后者在需要明确施法时应该是首选,除非您确定static_cast
会成功或reinterpret_cast
将失败.即使这样,也要考虑更长,更明确的选择.
执行a时static_cast
,C风格的强制转换也会忽略访问控制,这意味着它们可以执行其他强制转换无法执行的操作.不过,这主要是一个kludge,在我看来,这只是避免C风格演员阵容的另一个原因.
使用dynamic_cast
一个继承层次内进行转化的指针/引用.
使用static_cast
普通类型转换.
使用reinterpret_cast
的位模式的低级别重读.使用时要格外小心.
使用const_cast
铸造远const/volatile
.除非你使用const不正确的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 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(pData); ..... }
dynamic_cast:
void DebugLog::OnMessage(Message *msg) { static DebugMsgData *debug; static XYZMsgData *xyz; if(debug = dynamic_cast(msg->pdata)){ // debug message } else if(xyz = dynamic_cast (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(Passwd)
reinterpret_cast:
typedef unsigned short uint16; // Read Bytes returns that 2 bytes got read. bool ByteBuffer::ReadUInt16(uint16& val) { return ReadBytes(reinterpret_cast(&val), 2); }
如果你对内部有点了解,这可能会有所帮助......
的static_cast
C++编译器已经知道如何在诸如float之类的scaler类型之间进行转换.使用static_cast
他们.
当你要求编译器从类型转换A
为B
,static_cast
调用B
的构造函数A
作为参数传递.或者,A
可以有转换运算符(即A::operator B()
).如果B
没有这样的构造函数,或者A
没有转换运算符,则会出现编译时错误.
从演员A*
到B*
总是会成功,如果A和B是在继承层次结构(或无效),否则你会得到编译错误.
问题:如果你将基指针转换为派生指针,但如果实际对象不是真正的派生类型,那么你不会得到错误.你得到错误的指针,很可能是运行时的段错误.这同样适用于A&
对B&
.
Gotcha:从Derived转换为Base或者反之,创建新副本!对于来自C#/ Java的人来说,这可能是一个巨大的惊喜,因为结果基本上是从Derived创建的切断对象.
的dynamic_cast
dynamic_cast使用运行时类型信息来确定强制转换是否有效.例如,(Base*)
到(Derived*)
如果指针实际上不是派生类型的可能失败.
这意味着,与static_cast相比,dynamic_cast非常昂贵!
对于A*
to B*
,如果强制转换无效,那么dynamic_cast将返回nullptr.
对于A&
要B&
投的是无效的,那么将dynamic_cast会抛出bad_cast异常.
与其他演员表不同,存在运行时开销.
const_cast会
虽然static_cast可以对const执行非const,但它不能反过来.const_cast可以做到两种方式.
这方面的一个例子就是迭代一些容器set
,只返回它的元素作为const,以确保你不改变它的键.但是,如果您的意图是修改对象的非关键成员,那么它应该没问题.您可以使用const_cast删除constness.
另一个例子是,当你想实现T& SomeClass::foo()
以及const T& SomeClass::foo() const
.为避免代码重复,可以应用const_cast从一个函数返回另一个函数的值.
reinterpret_cast的
这基本上说在这个内存位置取这些字节并将其视为给定对象.
例如,您可以将4个字节的float加载到4个字节的int中,以查看float中的位是什么样的.
显然,如果数据类型不正确,您可能会遇到段错误.
此强制转换没有运行时开销.
请问这个回答你的问题?
我从来没有使用过reinterpret_cast
,并且想知道是否遇到需要它的情况并不是一种糟糕的设计气味.在我工作的代码库dynamic_cast
中使用了很多.与之不同的 static_cast
是dynamic_cast
运行时检查可能(更安全)或不可能(更多开销)是您想要的(参见msdn).
除了到目前为止的其他答案,这里是不明显的例子,其中static_cast
不足以满足reinterpret_cast
需要.假设有一个函数在输出参数中返回指向不同类的对象(不共享公共基类)的指针.这种函数的一个真实例子是CoCreateInstance()
(参见最后一个参数,实际上是这个参数void**
).假设您从此函数请求特定的对象类,因此您事先知道指针的类型(您经常为COM对象执行此操作).在这种情况下,你可以不投指针鼠标指针变成void**
有static_cast
:你需要reinterpret_cast
.
在代码中:
#include#include ..... INetFwPolicy2* pNetFwPolicy2 = nullptr; HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr, CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2), //static_cast (&pNetFwPolicy2) would give a compile error reinterpret_cast (&pNetFwPolicy2) );
但是,static_cast
适用于简单指针(不是指针指针),因此可以通过reinterpret_cast
以下方式重写上述代码以避免(以额外变量为代价):
#include#include ..... INetFwPolicy2* pNetFwPolicy2 = nullptr; void* tmp = nullptr; HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr, CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2), &tmp ); pNetFwPolicy2 = static_cast (tmp);
而其他的答案很好地描述C++强制转换之间的所有差异,我想补充一个简短的说明,为什么你不应该使用C-风格的转换(Type) var
和Type(var)
.
对于C++初学者来说,C风格的强制转换看起来就像是对C++强制转换的超集操作(static_cast <>(),dynamic_cast <>(),const_cast <>(),reinterpret_cast <>())而且有人可能比C++强制转换更喜欢它们.事实上,C风格的演员阵容是超集,写作也更短.
C风格演员表的主要问题是它们隐藏了演员的真实意图.C样式的强制类型可以执行几乎所有类型的强制转换,从static_cast <>()和dynamic_cast <>()完成的常规安全强制转换到const_cast <>()等潜在危险的强制转换,其中const修饰符可以被删除,因此const变量可以修改和reinterpret_cast <>(),甚至可以将整数值重新解释为指针.
这是样本.
int a=rand(); // Random number. int* pa1=reinterpret_cast(a); // OK. Here developer clearly expressed he wanted to do this potentially dangerous operation. int* pa2=static_cast (a); // Compiler error. int* pa3=dynamic_cast (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++的作者)一书的简短引用,该书出自The C++ Programming Language第4版 - 第302页.
这种C风格的转换比命名的转换运算符危险得多,因为在大型程序中更难以发现符号,并且程序员想要的转换类型并不明确.