常规演员与 static_cast 与 dynamic_cast

新手上路,请多包涵

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

MyClass *m = (MyClass *)ptr;

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

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

原文由 Graeme Perrow 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 692
2 个回答

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_castdynamic_cast 始终有效,并且也没有任何转换,因为“向上转换”是隐式的转换(假设基类是可访问的,即它是 public 继承)。

常规演员表

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

此外,C 风格的转换不仅允许您执行此操作,而且还允许您安全地转换为私有基类,而“等效” static_cast 序列将为您提供编译时间错误。

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

原文由 Johannes Schaub - litb 发布,翻译遵循 CC BY-SA 4.0 许可协议

静态演员表

静态转换执行兼容类型之间的转换。它类似于 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

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

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

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

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

然后可以使用 const 为函数传递一个常量变量。

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

print(nonConst); // allowed

来源和更多解释

原文由 Hossein 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题