以下各项的正确用途是什么:
静态铸造动态铸造常量_成本重新解释(_C)(type)值(C样式转换)类型(值)(函数样式转换)
如何决定在哪些特定情况下使用哪一种?
以下各项的正确用途是什么:
静态铸造动态铸造常量_成本重新解释(_C)(type)值(C样式转换)类型(值)(函数样式转换)
如何决定在哪些特定情况下使用哪一种?
当前回答
让我们在一个示例中看看reinterpret_cast和static_cast的区别:
#include <iostream>
using namespace std;
class A
{
int a;
};
class B
{
int b;
};
class C : public A, public B
{
int c;
};
int main()
{
{
B b;
cout << &b << endl;
cout << static_cast<C *>(&b) << endl; // 1
cout << reinterpret_cast<C *>(&b) << endl; // 2
}
cout << endl;
{
C c;
cout << &c << endl;
cout << static_cast<B *>(&c) << endl; // 3
cout << reinterpret_cast<B *>(&c) << endl; // 4
}
cout << endl;
{
A a;
cout << &a << endl;
cout << static_cast<C *>(&a) << endl;
cout << reinterpret_cast<C *>(&a) << endl;
}
cout << endl;
{
C c;
cout << &c << endl;
cout << static_cast<A *>(&c) << endl;
cout << reinterpret_cast<A *>(&c) << endl;
}
return 0;
}
生成输出:
0x7ffcede34f0c
0x7ffcede34f08 // 1
0x7ffcede34f0c // 2
0x7ffcede34f0c
0x7ffcede34f10 // 3
0x7ffcede34f0c // 4
0x7ffcede34f0c
0x7ffcede34f0c
0x7ffcede34f0c
0x7ffcede34f0c
0x7ffcede34f0c
0x7ffcede34f0c
注意,输出1和2以及3和4是不同的。为什么?在这两种情况下,其中一种是static_cast,另一种是对相同类型的相同输入重新解释cast。
情况可以在下图中看到:
C包含一个B,但B的起始地址与C不同。static_cast正确地计算了C中B的地址。然而,reinterpret_cast返回了我们作为输入给出的相同地址,这在这种情况下是不正确的:该地址没有B。
然而,当在A指针和C指针之间转换时,两个强制转换都返回相同的结果,因为它们恰好在相同的位置开始,顺便说一句,标准无论如何也不能保证这一点。
其他回答
staticcast是您应该尝试使用的第一个强制转换。它执行类型之间的隐式转换(例如int到float,或指向void*的指针),还可以调用显式转换函数(或隐式转换函数)。在许多情况下,显式声明static_cast是不必要的,但需要注意的是,t(something)语法相当于(t)something,应该避免(稍后将详细介绍)。然而,T(something,something_else)是安全的,并保证调用构造函数。
staticcast还可以通过继承层次结构进行转换。当向上转换(向基类)时,这是不必要的,但当向下转换时,只要不通过虚拟继承进行转换,就可以使用它。但是,它不进行检查,并且将层次结构静态转换为实际上不是对象类型的类型是未定义的行为。
const_cast可用于移除或添加常量到变量;没有其他C++转换能够移除它(甚至没有重新解释cast)。需要注意的是,只有当原始变量为常量时,修改以前的常量值才是未定义的;如果使用它来删除对未使用const声明的对象的引用的const,那么它是安全的。例如,当基于常量重载成员函数时,这很有用。它还可以用于向对象添加常量,例如调用成员函数重载。
constcast在volatile上也有类似的作用,尽管这不太常见。
dynamiccast专门用于处理多态性。您可以将指向任何多态类型的指针或引用强制转换为任何其他类类型(多态类型至少有一个声明或继承的虚拟函数)。你可以使用它不仅仅是向下投掷——你可以向侧面投掷,甚至向上投掷另一个链条。dynamic_cast将查找所需的对象,并在可能的情况下将其返回。如果不能,它将在指针的情况下返回nullptr,或者在引用的情况下抛出std::bad_cast。
不过dynamiccast有一些限制。如果继承层次结构中有多个相同类型的对象(所谓的“可怕的钻石”),并且您没有使用虚拟继承,那么它就不起作用。它也只能通过公共继承——它总是无法通过受保护或私人继承。然而,这很少是一个问题,因为这种形式的继承是罕见的。
reinterprecast是最危险的演员,应该非常谨慎地使用。它将一种类型直接转换为另一种类型,例如将值从一个指针转换为另另一个指针,或将指针存储在int中,或其他各种讨厌的事情。在很大程度上,使用reinterpret_cast可以得到的唯一保证是,通常情况下,如果将结果强制转换回原始类型,将得到完全相同的值(但如果中间类型小于原始类型,则不会)。还有许多转换是reinterpret_cast无法完成的。它主要用于特别奇怪的转换和位操作,例如将原始数据流转换为实际数据,或将数据存储在指向对齐数据的指针的低位。
C样式转换和函数样式转换分别是使用(类型)对象或类型(对象)的转换,在功能上是等效的。它们被定义为以下成功的第一个:
常量_成本static_cast(尽管忽略访问限制)static_cast(见上文),然后是const_cast重新解释(_C)重新解释成本,然后常量成本
因此,在某些情况下,它可以用作其他强制转换的替代品,但可能会非常危险,因为它可以转换为重新解释强制转换,当需要显式强制转换时,应首选后者,除非您确定static_cast将成功或重新解释强制。即使如此,也要考虑更长、更明确的选项。
C样式转换在执行static_cast时也会忽略访问控制,这意味着它们能够执行其他转换无法执行的操作。不过,这基本上是一个拙劣的说法,在我看来,这只是避免C型造型的另一个原因。
让我们在一个示例中看看reinterpret_cast和static_cast的区别:
#include <iostream>
using namespace std;
class A
{
int a;
};
class B
{
int b;
};
class C : public A, public B
{
int c;
};
int main()
{
{
B b;
cout << &b << endl;
cout << static_cast<C *>(&b) << endl; // 1
cout << reinterpret_cast<C *>(&b) << endl; // 2
}
cout << endl;
{
C c;
cout << &c << endl;
cout << static_cast<B *>(&c) << endl; // 3
cout << reinterpret_cast<B *>(&c) << endl; // 4
}
cout << endl;
{
A a;
cout << &a << endl;
cout << static_cast<C *>(&a) << endl;
cout << reinterpret_cast<C *>(&a) << endl;
}
cout << endl;
{
C c;
cout << &c << endl;
cout << static_cast<A *>(&c) << endl;
cout << reinterpret_cast<A *>(&c) << endl;
}
return 0;
}
生成输出:
0x7ffcede34f0c
0x7ffcede34f08 // 1
0x7ffcede34f0c // 2
0x7ffcede34f0c
0x7ffcede34f10 // 3
0x7ffcede34f0c // 4
0x7ffcede34f0c
0x7ffcede34f0c
0x7ffcede34f0c
0x7ffcede34f0c
0x7ffcede34f0c
0x7ffcede34f0c
注意,输出1和2以及3和4是不同的。为什么?在这两种情况下,其中一种是static_cast,另一种是对相同类型的相同输入重新解释cast。
情况可以在下图中看到:
C包含一个B,但B的起始地址与C不同。static_cast正确地计算了C中B的地址。然而,reinterpret_cast返回了我们作为输入给出的相同地址,这在这种情况下是不正确的:该地址没有B。
然而,当在A指针和C指针之间转换时,两个强制转换都返回相同的结果,因为它们恰好在相同的位置开始,顺便说一句,标准无论如何也不能保证这一点。
使用dynamic_cast转换继承层次结构中的指针/引用。对普通类型转换使用static_cast。使用reinterpret_cast对位模式进行低级重新解释。使用时要格外小心。使用const_cast丢弃const/vatile。避免这种情况,除非您使用的是常量错误的API。
(上面给出了大量的理论和概念解释)
下面是我使用static_cast、dynamic_cast、const_cast、reinterpret_cast时的一些实际示例。
(也可参考此来理解解释:http://www.cplusplus.com/doc/tutorial/typecasting/)
静态铸造:
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);
.....
}
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( ... )*/{
// ...
}
}
常量成本(_C):
// *Passwd declared as a const
const unsigned char *Passwd
// on some situation it require to remove its constness
const_cast<unsigned char*>(Passwd)
重新解释(_C):
typedef unsigned short uint16;
// Read Bytes returns that 2 bytes got read.
bool ByteBuffer::ReadUInt16(uint16& val) {
return ReadBytes(reinterpret_cast<char*>(&val), 2);
}
为了理解,让我们考虑下面的代码片段:
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将指向对象的指针转换为指向任何无关对象类型的指针。
需要注意的一点是:dynamic_cast在运行时会失败,但在大多数编译器上,它也会编译失败,因为被转换的指针的结构中没有虚拟函数,这意味着dynamic_cast只能与多态类指针一起工作。
何时使用C++转换:
使用static_cast等效于进行值转换的C样式转换,或者当我们需要将指针从类显式上转换到其超类时。使用const_cast删除const限定符。使用reinterpret_cast执行指针类型与整数和其他指针类型之间的不安全转换。只有当我们知道自己在做什么并且了解别名问题时,才能使用此选项。