题 常规演员与static_cast与dynamic_cast [重复]


这个问题在这里已有答案:

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

MyClass *m = (MyClass *)ptr;

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

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

1472
2017-08-26 13:20


起源


另见<stackoverflow.com/questions/28080/...; - Sam Hasler
我不会把传统的C风格演员称为C ++中的“常规演员”,因为它不是。你通常不应该在C ++中使用,特别是对于类,它很容易犯错误。使用它是C程序员已经转向C ++但尚未学习C ++的标志。 - hyde
一个问题的答案怎么能是一个问题的副本没有答案?甚至更多的是,这个问题早在“原始”被问到 - Vladp
@Vladp 万一你还在想,或者其他人正在读这个并且想知道。 (另外,为了记录,它不是一个关闭它的主持人,它是一个用户 欺骗锤) - Nic Hartley
仅供参考 相关问题 有更多的赞成,答案也有更多的赞成。关联的问题也有一些 良好的非理论范例。 (此外,链接的问题不会错误地将C风格的类型转换语法称为“常规强制转换”。) - Trevor Boyd Smith


答案:


的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
}

“up-cast”(强制转换为基类)始终对两者都有效 static_cast 和 dynamic_cast,并且没有任何演员阵容,因为“向上演员”是隐式转换。

常规演员

这些演员阵容也被称为C型演员。 C风格的强制转换基本上与尝试C ++强制转换的一系列序列完全相同,并且在没有考虑过的情况下采用第一个可行的C ++强制转换 dynamic_cast。毋庸置疑,这更加强大,因为它结合了所有 const_caststatic_cast 和 reinterpret_cast,但它也不安全,因为它不使用 dynamic_cast

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

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


1413
2017-08-10 13:50



另请参阅boost的另外两个演员表: boost.org/doc/libs/1_47_0/libs/conversion/... - Neil G
@ JohannesSchaub-litb:你确定C风格的演员会让你“安全地”演绎到一个私人基类吗?我可以看到,当私有基类是唯一的/ base /时,工作,但虚拟/多重继承呢?我假设C样式转换没有指针操作。 - Joseph Garvin
@ JohannesSchaub-litb是否真的存在使用旧的C风格演员而不是C ++演员阵容的一些开销? - xcrypt
@Joseph:它不会正确地进行交叉投射,或者需要运行时检查的任何其他情况(dynamic_cast 是必须的)。但它会做所有相同的指针调整 static_cast 确实。支持多个(非虚拟)继承,并且将使用正确的指针调整。 - Ben Voigt
你能更详细地解释为什么动态演员表中的向下转换是无效的吗?承担 Derived 有一个 member m 我想达成,如果能实现这一目标 dynamic_cast 不是一个选择? - ted


静态演员

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

这个主要用于添加或删除变量的const修饰符。

const int myConst = 5;
int *nonConst = const_cast<int*>(&myConst); // removes const

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

*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

来源和更多解释


118
2017-08-26 13:28



std::bad_cast 定义于 <typeinfo> - Evgeny
很棒的答案!得知吨:) - will.fiset


你应该看一下这篇文章 C ++编程/类型转换

它包含所有不同演员类型的良好描述。以下摘自以上链接:

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的

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


71
2017-09-19 17:30





避免使用C风格的演员阵容。

C样式转换是const和重新解释转换的混合,在代码中很难找到和替换。 C ++应用程序程序员应该避免使用C风格的转换。


23
2017-08-26 13:39





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

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

根据你的意愿采取建议。我远不是一个C ++大师。


21
2017-08-26 13:38



Bjarne Stroustrup ......那个给我们这个类型铸造地狱的人呢?
^是的,因为明确标记并故意局限于明确定义的角色的C ++强制转换比C强制转换更“恶劣”,它只是盲目地尝试多种类型的强制转换,直到 什么 工作,无论感觉如何......好的。 - underscore_d


C风格的强制转换会混淆const_cast,static_cast和reinterpret_cast。

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


10
2017-08-26 13:26





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


8
2018-02-05 17:10





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

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


7