我写C和C++代码已经快二十年了,但这些语言中有一个方面我从未真正理解过。我显然使用了常规的石膏,即。
MyClass *m = (MyClass *)ptr;
到处都是,但似乎有两种其他类型的演员,我不知道有什么不同。以下代码行之间有什么区别?
MyClass *m = (MyClass *)ptr;
MyClass *m = static_cast<MyClass *>(ptr);
MyClass *m = dynamic_cast<MyClass *>(ptr);
我写C和C++代码已经快二十年了,但这些语言中有一个方面我从未真正理解过。我显然使用了常规的石膏,即。
MyClass *m = (MyClass *)ptr;
到处都是,但似乎有两种其他类型的演员,我不知道有什么不同。以下代码行之间有什么区别?
MyClass *m = (MyClass *)ptr;
MyClass *m = static_cast<MyClass *>(ptr);
MyClass *m = dynamic_cast<MyClass *>(ptr);
当前回答
dynamic_cast仅支持指针和引用类型。如果类型是指针,则无法强制转换,则返回NULL;如果类型是引用类型,则抛出异常。因此,dynamic_cast可以用于检查对象是否为给定类型,而static_cast不能(您只会得到一个无效值)。
C风格(和其他)演员阵容在其他答案中也有提及。
其他回答
静态铸造
The static cast performs conversions between compatible types. It is similar to the C-style cast, but is more restrictive. For example, the C-style cast would allow an integer pointer to point to a char.char c = 10; // 1 byte
int *p = (int*)&c; // 4 bytes
由于这会导致一个4字节的指针指向1字节的已分配内存,因此写入该指针将导致运行时错误或覆盖某些相邻内存。
*p = 5; // run-time error: stack corruption
与C样式转换不同,静态转换将允许编译器检查指针和指针对象数据类型是否兼容,这允许程序员在编译期间捕获这种错误的指针分配。
int *q = static_cast<int*>(&c); // compile-time error
重新解释演员
为了强制指针转换,与C样式转换在背景中的方式相同,将使用重新解释转换。
int *r = reinterpret_cast<int*>(&c); // forced conversion
此强制转换处理某些不相关类型之间的转换,例如从一个指针类型转换为另一个不兼容的指针类型。它将简单地执行数据的二进制拷贝,而不改变底层的位模式。注意,这种低级操作的结果是系统特定的,因此不可移植。如果不能完全避免,则应谨慎使用。
动态铸造
此函数仅用于将对象指针和对象引用转换为继承层次结构中的其他指针或引用类型。通过执行运行时检查指针是否引用了目标类型的完整对象,这是唯一一个确保可以转换指向的对象的强制转换。要进行此运行时检查,对象必须是多态的。也就是说,类必须定义或继承至少一个虚拟函数。这是因为编译器将只为此类对象生成所需的运行时类型信息。
动态铸造示例
在下面的示例中,使用动态转换将MyChild指针转换为MyBase指针。此派生到基的转换成功,因为Child对象包含一个完整的base对象。
class MyBase
{
public:
virtual void test() {}
};
class MyChild : public MyBase {};
int main()
{
MyChild *child = new MyChild();
MyBase *base = dynamic_cast<MyBase*>(child); // ok
}
下一个示例尝试将MyBase指针转换为MyChild指针。由于Base对象不包含完整的Child对象,因此指针转换将失败。为此,动态强制转换返回一个空指针。这提供了一种在运行时检查转换是否成功的方便方法。
MyBase *base = new MyBase();
MyChild *child = dynamic_cast<MyChild*>(base);
if (child == 0)
std::cout << "Null pointer returned";
如果转换引用而不是指针,则动态强制转换将失败,引发bad_cast异常。这需要使用try-catch语句来处理。
#include <exception>
// …
try
{
MyChild &child = dynamic_cast<MyChild&>(*base);
}
catch(std::bad_cast &e)
{
std::cout << e.what(); // bad dynamic_cast
}
动态或静态铸造
使用动态转换的优点是,它允许程序员在运行时检查转换是否成功。缺点是执行此检查会产生性能开销。出于这个原因,在第一个示例中使用静态强制转换是更好的,因为派生到基转换永远不会失败。
MyBase *base = static_cast<MyBase*>(child); // ok
然而,在第二个示例中,转换可能成功或失败。如果MyBase对象包含MyBase实例,它将失败;如果它包含MyChild实例,它会成功。在某些情况下,这可能直到运行时才知道。在这种情况下,动态转换比静态转换更好。
// Succeeds for a MyChild object
MyChild *child = dynamic_cast<MyChild*>(base);
如果使用静态强制转换而不是动态强制转换来执行基到派生的转换,则转换不会失败。它将返回一个指向不完整对象的指针。取消引用这样的指针可能会导致运行时错误。
// Allowed, but invalid
MyChild *child = static_cast<MyChild*>(base);
// Incomplete MyChild object dereferenced
(*child);
Const铸造
此选项主要用于添加或删除变量的常量修饰符。
const int myConst = 5;
int *nonConst = const_cast<int*>(&myConst); // removes const
虽然常量转换允许更改常量的值,但这样做仍然是无效的代码,可能会导致运行时错误。例如,如果常量位于只读存储器的一部分中,则可能发生这种情况。
*nonConst = 10; // potential run-time error
const强制转换主要在函数采用非常量指针参数时使用,即使它不修改指针对象。
void print(int *p)
{
std::cout << *p;
}
然后可以通过使用常量强制转换向函数传递常量变量。
print(&myConst); // error: cannot convert
// const int* to int*
print(nonConst); // allowed
来源和更多解释
dynamic_cast仅支持指针和引用类型。如果类型是指针,则无法强制转换,则返回NULL;如果类型是引用类型,则抛出异常。因此,dynamic_cast可以用于检查对象是否为给定类型,而static_cast不能(您只会得到一个无效值)。
C风格(和其他)演员阵容在其他答案中也有提及。
静态铸造
staticcast用于您基本上想要反转隐式转换的情况,但有一些限制和添加。staticcast不执行运行时检查。如果您知道您引用的是特定类型的对象,因此不需要进行检查,则应使用此选项。例子:
void func(void *data) {
// Conversion from MyClass* -> void* is implicit
MyClass *c = static_cast<MyClass*>(data);
...
}
int main() {
MyClass c;
start_thread(&func, &c) // func(&c) will be called
.join();
}
在本例中,您知道您传递了MyClass对象,因此不需要运行时检查来确保这一点。
动态铸造
dynamic_cast在您不知道对象的动态类型时非常有用。如果引用的对象不包含作为基类转换为的类型,则它将返回空指针(在这种情况下,当您转换为引用时,会引发bad_cast异常)。
if (JumpStm *j = dynamic_cast<JumpStm*>(&stm)) {
...
} else if (ExprStm *e = dynamic_cast<ExprStm*>(&stm)) {
...
}
如果参数类型不是多态的,则不能使用dynamic_cast进行下变频(转换为派生类)。例如,以下代码无效,因为Base不包含任何虚拟函数:
struct Base { };
struct Derived : Base { };
int main() {
Derived d; Base *b = &d;
dynamic_cast<Derived*>(b); // Invalid
}
“向上转换”(向基类转换)对于static_cast和dynamic_cast始终有效,而且也没有任何转换,因为“向上”是隐式转换(假设基类是可访问的,即它是公共继承)。
常规铸造
这些造型也称为C型造型。C风格的强制转换基本上与尝试一系列C++强制转换序列相同,并采用第一个有效的C++强制转换,而不考虑dynamic_cast。不用说,这是更强大的,因为它结合了const_cast、static_cast和reinterpret_cast,但它也不安全,因为它不使用dynamic_cast。
此外,C样式转换不仅允许您这样做,而且还允许您安全地转换到私有基类,而“等效”的static_cast序列会给您带来编译时错误。
有些人更喜欢C风格的演员,因为他们简洁。我只将它们用于数字强制转换,当涉及用户定义的类型时,使用适当的C++强制转换,因为它们提供了更严格的检查。
避免使用C样式转换。
C样式转换是常量转换和重新解释转换的混合,很难在代码中找到和替换。C++应用程序程序员应该避免C样式转换。
仅供参考,我相信Bjarne Stroustrup曾说过,应该避免C风格的强制转换,如果可能的话,应该使用static_cast或dynamic_cast。
Barne Stroustrup的C++风格常见问题解答
你愿意接受那个建议吗。我远非C++大师。