我已经写了将近20年的C和C++代码,但是这些语言中有一个方面我从来没有真正理解过。显然我使用过常规转换,即
MyClass *m = (MyClass *)ptr;
代码中有很多转换,但似乎还有两种其他类型的转换,我不知道它们之间的区别。以下代码行有什么区别?
MyClass *m = (MyClass *)ptr;
MyClass *m = static_cast<MyClass *>(ptr);
MyClass *m = dynamic_cast<MyClass *>(ptr);
我已经写了将近20年的C和C++代码,但是这些语言中有一个方面我从来没有真正理解过。显然我使用过常规转换,即
MyClass *m = (MyClass *)ptr;
代码中有很多转换,但似乎还有两种其他类型的转换,我不知道它们之间的区别。以下代码行有什么区别?
MyClass *m = (MyClass *)ptr;
MyClass *m = static_cast<MyClass *>(ptr);
MyClass *m = dynamic_cast<MyClass *>(ptr);
static_cast
用于需要撤销隐式转换的情况,但有一些限制和补充说明。 static_cast
不执行运行时检查。如果您知道自己引用特定类型的对象,则应使用此操作,因此检查是不必要的。例如:
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++转换,因为它们提供了更严格的检查。
Derived
有一个成员变量m
,如果不能使用dynamic_cast
,该如何访问它?请提供解决方案。我会尽力使翻译保持原意并更易懂。 - tedchar c = 10; // 1 byte
int *p = (int*)&c; // 4 bytes
由于这导致一个4字节指针指向1字节的分配内存,向该指针写入数据将引起运行时错误或覆盖相邻的内存。
*p = 5; // run-time error: stack corruption
int *q = static_cast<int*>(&c); // compile-time error
int *r = reinterpret_cast<int*>(&c); // forced conversion
class MyBase
{
public:
virtual void test() {}
};
class MyChild : public MyBase {};
int main()
{
MyChild *child = new MyChild();
MyBase *base = dynamic_cast<MyBase*>(child); // ok
}
MyBase *base = new MyBase();
MyChild *child = dynamic_cast<MyChild*>(base);
if (child == 0)
std::cout << "Null pointer returned";
#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
const
转换允许改变常量的值,但这样做仍然是无效的代码,可能会导致运行时错误。例如,如果常量位于只读内存段中,就可能发生这种情况。*nonConst = 10; // potential run-time error
void print(int *p)
{
std::cout << *p;
}
const
转换来传递一个常量变量。print(&myConst); // error: cannot convert
// const int* to int*
print(nonConst); // allowed
std::bad_cast
is defined in <typeinfo>
- EvgMyBase *base = child; // 可以
- Isidoro Ghezzi你应该查看文章C++编程/类型转换。
它包含了对所有不同类型的转换的良好描述。以下内容摘自上面的链接:
const_cast
const_cast(expression) 用于添加/删除变量的constness(或volatile性)。
static_cast
static_cast(expression) 用于在整数类型之间进行类型转换。例如 char->long, int->short 等。
Static cast还用于将指针转换为相关类型,例如将void*转换为适当的类型。
dynamic_cast
dynamic_cast用于在运行时转换指针和引用,通常用于将指针或引用向上或向下转换继承链(继承层次结构)。
dynamic_cast(expression)
目标类型必须是指针或引用类型,并且表达式必须求值为指针或引用。仅当表达式所引用的对象类型与目标类型兼容并且基类至少有一个虚成员函数时,dynamic_cast才起作用。如果不是这样,并且被转换的表达式类型是指针,则返回NULL;如果对引用进行动态转换失败,则抛出bad_cast异常。当它不失败时,dynamic_cast会将指向expression所引用对象的指针或引用返回到目标类型的对象。
reinterpret_cast
reinterpret_cast只是按位转换一种类型到另一种类型。任何指针或整数类型都可以使用reinterpret_cast转换为任何其他类型,因此很容易被误用。例如,使用reinterpret_cast,可以不安全地将整数指针转换为字符串指针。
供您参考,我相信Bjarne Stroustrup曾说过应该避免使用C-style cast,而应尽可能使用static_cast或dynamic_cast。
根据自己的需求采纳这个建议。我离成为C++大师还有很长的路要走。
避免使用C风格转换。
C风格转换是const和reinterpret转换的混合体,在代码中很难查找和替换。C++应用程序员应避免使用C风格转换。
C-style转换混淆了const_cast、static_cast和reinterpret_cast。
我希望C++没有C-style转换。 C++ 转换有明显的特征(正如它们应该有的那样; 转换通常表示正在执行不好的操作),并且正确区分转换执行的不同类型。 它们还允许编写类似的函数,例如 boost::lexical_cast,这在一致性方面非常好。
dynamic_cast
运行时进行类型检查,仅适用于引用和指针,而 static_cast
不提供运行时类型检查。有关完整信息,请参阅 MSDN 文章 static_cast 运算符。
dynamic_cast
只支持指针和引用类型。如果转换不可能,则返回 NULL
(对于指针类型),或者抛出异常(对于引用类型)。因此,dynamic_cast
可以用于检查对象是否为特定类型,而 static_cast
不能(你最终只会得到一个无效值)。
C风格(以及其他)的转换在其他答案中已经讨论过了。