【问题标题】:Regular cast vs. static_cast vs. dynamic_cast [duplicate]常规演员与 static_cast 与 dynamic_cast [重复]
【发布时间】:2010-09-06 21:06:58
【问题描述】:

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

MyClass *m = (MyClass *)ptr;

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

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

【问题讨论】:

  • 我不会将遗留的 C 风格转换称为 C++ 中的“常规转换”,因为它绝不是。你通常不应该在 C++ 中使用,尤其是在类中,它太容易出错了。使用它是一个 C 程序员已经转向 C++ 但还没有完全学会 C++ 的标志。
  • 一个有答案的问题怎么可能是一个没有答案的问题的重复?更重要的是,这个问题被问得比“原始”更早
  • @Vladp In case you're still wondering, or anyone else is reading this and wonders。 (另外,为了记录,关闭这个的不是版主,而是dupe-hammer的用户)
  • 仅供参考 linked question 有更多的赞成票,答案也有更多的赞成票。链接的问题也有一些good non-theoretical examples。 (此外,链接的问题并未错误地将 C 样式类型转换语法称为“常规转换”。)

标签: c++ pointers casting


【解决方案1】:

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++ 类型转换,因为它们提供更严格的检查。

【讨论】:

  • 另见 boost 的另外两个演员表:boost.org/doc/libs/1_47_0/libs/conversion/…
  • @JohannesSchaub-litb:您确定 C 风格的转换可以让您“安全地”转换为私有基类吗?我可以看到当私有基类是唯一的 /base/ 时可以工作,但是虚拟/多重继承呢?我假设 C 风格转换没有指针操作。
  • @JohannesSchaub-litb 使用旧的 c 样式转换而不是 C++ 转换确实会产生一些开销吗?
  • @Joseph:它不会正确进行交叉转换,或者需要运行时检查的任何其他情况(需要dynamic_cast)。但它会做与static_cast 一样的指针调整。支持多重(非虚拟)继承就好了,并且会使用正确的指针调整。
  • 您能否更详细地解释一下为什么动态转换部分中的向下转换无效?假设Derived 有一个member m 我想联系,如果dynamic_cast 不是一个选项,如何实现?
【解决方案2】:

静态转换

静态转换执行兼容类型之间的转换。它类似于 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 cast 允许更改常量的值,但这样做仍然是可能导致运行时错误的无效代码。例如,如果常量位于只读内存的一部分中,就会发生这种情况。

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

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

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

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

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

Source and More Explanations

【讨论】:

  • std::bad_cast 定义在&lt;typeinfo&gt;
  • 从子到基,不需要强制转换:MyBase *base = child; // ok
  • 在我看来,最好的答案,非常简单明了
  • 这应该是imo的最佳答案
【解决方案3】:

你应该看看文章C++ Programming/Type Casting

它很好地描述了所有不同的演员阵容。以下内容来自上述链接:

const_cast

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

static_cast

static_cast(expression) static_cast() 用于在 整数类型。 '例如。' char->long、int->short 等

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

dynamic_cast

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

dynamic_cast(表达式)

目标类型必须是指针或引用类型,并且 表达式必须计算为指针或引用。动态演员作品 仅当表达式所指的对象类型为 与目标类型兼容且基类至少有一个 虚成员函数。如果不是,以及被转换的表达式类型 是指针,如果对引用进行动态强制转换,则返回 NULL 失败,会抛出一个 bad_cast 异常。当它没有失败时,动态 cast 将目标类型的指针或引用返回到对象 引用哪个表达式。

reinterpret_cast

重新解释转换只是将一种类型按位转换为另一种类型。任何指针 或整数类型可以通过重新解释转换为任何其他类型, 容易被误用。例如,使用 reinterpret cast one 可能不安全地将整数指针转换为字符串指针。

【讨论】:

    【解决方案4】:

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

    Barne Stroustrup's C++ style FAQ

    根据您的意愿采纳该建议。我远非 C++ 大师。

    【讨论】:

    • ^ 是的,因为被明确标记并故意限制为明确定义的角色的 C++ 转换比 C 转换更“地狱”,它只是盲目地尝试多种类型的转换,直到 anything 有效,不管意义如何……很好。
    【解决方案5】:

    避免使用 C 样式转换。

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

    【讨论】:

      【解决方案6】:

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

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

      【讨论】:

        【解决方案7】:

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

        其他答案中已经涵盖了 C 样式(和其他)类型转换。

        【讨论】:

        • 你只会得到一个无效的值”和一个未定义的行为。也就是说,即使您不使用该值,程序也会出现异常
        【解决方案8】:

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

        【讨论】:

          猜你喜欢
          • 2010-11-18
          • 2011-06-06
          • 1970-01-01
          • 2014-05-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多