1920

我已经编写 C 和 C++ 代码将近 20 年了,但是这些语言的某一方面我从未真正理解过。我显然使用过常规演员,即

MyClass *m = (MyClass *)ptr;

到处都是,但似乎还有另外两种类型的演员表,我不知道有什么区别。以下代码行之间有什么区别?

MyClass *m = (MyClass *)ptr;
MyClass *m = static_cast<MyClass *>(ptr);
MyClass *m = dynamic_cast<MyClass *>(ptr);
4

8 回答 8

1757

static_cast

`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

当您不知道对象的动态类型是什么时,`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_castand有效dynamic_cast,并且也没有任何转换,因为“向上转换”是隐式转换(假设基类是可访问的,即它是public继承) .

常规演员表

这些演员表也称为 C 风格演员表。C 风格的强制转换基本上等同于尝试一系列 C++ 强制转换,并采用第一个有效的 C++ 强制转换,而无需考虑dynamic_cast. 不用说,这更强大,因为它结合了所有const_cast,static_castreinterpret_cast,但它也不安全,因为它不使用dynamic_cast.

此外,C 风格的转换不仅允许您这样做,而且还允许您安全地转换为私有基类,而“等效”static_cast序列会给您一个编译时错误。

有些人更喜欢 C 风格的演员,因为它们很简洁。我只将它们用于数字类型转换,并在涉及用户定义类型时使用适当的 C++ 类型转换,因为它们提供了更严格的检查。

于 2009-08-10T13:50:45.557 回答
245

静态演员表

静态转换执行兼容类型之间的转换。它类似于 C 风格的演员表,但更具限制性。例如,C 风格的转换将允许一个整数指针指向一个字符。
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

尽管constcast 允许更改常量的值,但这样做仍然是可能导致运行时错误的无效代码。例如,如果常量位于只读内存的一部分中,就会发生这种情况。

*nonConst = 10; // potential run-time error

constcast 主要用于当有一个函数采用非常量指针参数时,即使它不修改指针。

void print(int *p) 
{
   std::cout << *p;
}

然后可以使用强制转换为函数传递一个常量变量const

print(&myConst); // error: cannot convert 
                 // const int* to int*
 
print(nonConst); // allowed

来源和更多解释

于 2013-08-24T01:55:13.040 回答
89

您应该查看文章C++ Programming/Type Casting

它包含对所有不同类型转换的良好描述。以下内容来自上述链接:

const_cast

const_cast(expression) const_cast<>() 用于添加/删除变量的 const(ness)(或 volatile-ness)。

static_cast

static_cast(expression) static_cast<>() 用于在整数类型之间进行转换。'eg' char->long、int->short 等。

静态转换也用于将指针转换为相关类型,例如将 void* 转换为适当的类型。

dynamic_cast

动态转换用于在运行时转换指针和引用,通常用于将指针或引用向上或向下转换为继承链(继承层次结构)。

dynamic_cast(表达式)

目标类型必须是指针或引用类型,并且表达式必须计算为指针或引用。只有当表达式所引用的对象类型与目标类型兼容并且基类具有至少一个虚成员函数时,动态转换才有效。如果不是,并且被转换的表达式类型是指针,则返回 NULL,如果对引用的动态转换失败,则抛出 bad_cast 异常。当它没有失败时,动态转换将目标类型的指针或引用返回到表达式所引用的对象。

reinterpret_cast

Reinterpret cast 只是将一种类型按位转换为另一种类型。任何指针或整数类型都可以通过 reinterpret cast 转换为任何其他类型,很容易导致误用。例如,使用 reinterpret cast 时,可能会不安全地将整数指针转换为字符串指针。

于 2008-08-26T13:28:43.793 回答
33

仅供参考,我相信引用 Bjarne Stroustrup 的话说,要避免 C 风格的强制转换,如果可能的话,你应该使用 static_cast 或 dynamic_cast。

Barne Stroustrup 的 C++ 风格常见问题解答

尽你所能接受这个建议。我远非 C++ 大师。

于 2008-08-26T13:39:35.950 回答
29

避免使用 C 样式转换。

C 风格的转换是 const 和 reinterpret 转换的混合体,很难在代码中查找和替换。C++ 应用程序程序员应该避免 C 风格的强制转换。

于 2008-09-19T17:30:24.813 回答
15

C 风格的转换将 const_cast、static_cast 和 reinterpret_cast 混为一谈。

我希望 C++ 没有 C 风格的演员表。C++ 强制转换正确地突出(应该如此;强制转换通常表示做坏事)并正确区分强制转换执行的不同类型的转换。它们还允许编写外观相似的函数,例如 boost::lexical_cast,从一致性的角度来看这是非常好的。

于 2008-08-26T13:38:35.360 回答
13

dynamic_cast仅支持指针和引用类型。如果类型是指针,则如果强制转换是不可能的,则返回NULL;如果类型是引用类型,则抛出异常。因此,dynamic_cast可用于检查对象是否属于给定类型,static_cast不能(您最终会得到无效值)。

其他答案中已经涵盖了 C 风格(和其他)演员表。

于 2012-02-05T17:10:25.903 回答
12

dynamic_cast具有运行时类型检查,仅适用于引用和指针,static_cast而不提供运行时类型检查。有关完整信息,请参阅 MSDN 文章static_cast Operator

于 2008-08-26T13:26:23.707 回答