常规演员 vs. static_cast vs. dynamic_cast [重复]

Regular cast vs. static_cast vs. dynamic_cast [duplicate]

提问人:Graeme Perrow 提问时间:8/26/2008 最后编辑:R. Martinho FernandesGraeme Perrow 更新时间:7/25/2023 访问量:892839

问:

这个问题在这里已经有答案了:
9年前关闭。

去年,社群审查了是否要重新讨论这个问题,并关闭了这个问题:

原始关闭原因未解决

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

MyClass *m = (MyClass *)ptr;

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

MyClass *m = (MyClass *)ptr;
MyClass *m = static_cast<MyClass *>(ptr);
MyClass *m = dynamic_cast<MyClass *>(ptr);
C++ 指针 强制转换

评论

78赞 hyde 1/30/2013
我不会将传统的 C 样式转换称为 C++ 中的“常规转换”,因为它绝非如此。你通常不应该在 C++ 中使用,尤其是类,它太容易出错了。使用它是 C 程序员的标志,他已经转向 C++ 但还没有完全学习 C++。
7赞 2/1/2017
@Vladp 如果您仍然想知道,或者其他人正在阅读本文并想知道。(另外,作为记录,关闭这个的不是版主,而是一个拿着骗子锤的用户)
5赞 Trevor Boyd Smith 3/7/2018
仅供参考,链接的问题有更多的赞成票,答案也有更多的赞成票。此外,链接的问题也有一些很好的非理论示例。(此外,链接的问题不会错误地将 C 样式类型转换语法称为“常规转换”。

答:

14赞 Inisheer 8/26/2008 #1

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

92赞 TJ Seabrooks 8/26/2008 #2

您应该查看文章 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

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

15赞 DrPizza 8/26/2008 #3

C 型演员将 const_cast、static_cast 和 reinterpret_cast 混为一谈。

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

35赞 Jason Baker 8/26/2008 #4

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

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

随心所欲地接受这个建议。我远不是C++大师。

评论

14赞 underscore_d 4/13/2016
^ 是的,因为明确标记并故意限制在明确定义的角色中的 C++ 强制转换比 C 强制转换更“地狱”,后者只是盲目地尝试多种类型的强制转换,直到任何事情都有效,无论意义如何......好一个。
0赞 Bill Weinman 12/31/2021
实际上,如果你读过他的常见问题解答,Stroustrup 建议你避免一起投射。他关于static_cast的一节的第一句话是:“通常最好避免石膏。
0赞 blubberdiblub 1/12/2022
@BillWeinman在实践中,你不能完全避免石膏(就我而言,“最好避免”的措辞允许这样做)。一旦您将自己的代码与 API 或不同的 API 相互连接,您通常会遇到类型不完全匹配的情况,您将不得不求助于强制转换。对于较旧的和有机生长的 API 尤其如此。WinAPI 就是一个很好的例子。
28赞 ugasoft 9/20/2008 #5

避免使用 C 样式转换。

C 样式强制转换是常量强制转换和重新解释强制转换的混合体,很难在代码中找到和替换。C++ 应用程序程序员应避免 C 样式强制转换。

1844赞 Johannes Schaub - litb 8/10/2009 #6

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当您不知道对象的动态类型是什么时,这很有用。如果引用的对象不包含作为基类强制转换为的类型,则返回 null 指针(强制转换为引用时,在这种情况下会引发异常)。bad_cast

if (JumpStm *j = dynamic_cast<JumpStm*>(&stm)) {
  ...
} else if (ExprStm *e = dynamic_cast<ExprStm*>(&stm)) {
  ...
}

如果参数类型不是多态的,则不能用于向下转换(强制转换为派生类)。例如,以下代码无效,因为它不包含任何虚拟函数:dynamic_castBase

struct Base { };
struct Derived : Base { };
int main() {
  Derived d; Base *b = &d;
  dynamic_cast<Derived*>(b); // Invalid
}

“up-cast”(强制转换为基类)对 和 始终有效,并且没有任何强制转换,因为“up-cast”是一种隐式转换(假设基类是可访问的,即它是一个继承)。static_castdynamic_castpublic

常规演员表

这些铸件也称为 C 型铸件。C 样式转换基本上与尝试一系列 C++ 转换序列相同,并采用第一个有效的 C++ 转换,而无需考虑 .毋庸置疑,这功能更强大,因为它结合了所有 、 和 ,但它也不安全,因为它不使用 .dynamic_castconst_caststatic_castreinterpret_castdynamic_cast

此外,C 样式强制转换不仅允许你这样做,而且还允许你安全地强制转换为私有基类,而“等效”序列会为此提供编译时错误。static_cast

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

评论

14赞 Neil G 8/11/2011
另请参阅助推器的两个附加施法:boost.org/doc/libs/1_47_0/libs/conversion/...
3赞 Joseph Garvin 3/1/2012
@JohannesSchaub-litb:你确定 C 风格的强制转换可以让你“安全地”强制转换为私有基类吗?我可以看到当私有基类是唯一的/base/时可以工作,但是虚拟/多重继承呢?我假设 C 样式强制转换不执行指针操作。
2赞 xcrypt 5/11/2012
@JohannesSchaub litb:在C++转换上使用旧的 C 样式转换是否也涉及一些开销?
1赞 Ben Voigt 12/29/2012
@Joseph:它不会正确地进行交叉转换,或者任何其他需要运行时检查(需要)的情况。但它将执行所有相同的指针调整。支持多重(非虚拟)继承,并且将使用正确的指针调整。dynamic_caststatic_cast
4赞 ted 3/9/2013
您能更详细地解释一下为什么动态投射部分中的下沉是无效的吗?假设有一个我想达到的,如果不是一个选项,这将如何实现?Derivedmember mdynamic_cast
13赞 larsmoa 2/6/2012 #7

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

C型(和其他)演员表已在其他答案中介绍。

评论

1赞 curiousguy 11/3/2019
"你最终只会得到一个无效的值“和一个未定义的行为。也就是说,即使您不使用该值,程序也存在不当行为
285赞 5 revs, 3 users 96%Hossein #8

静态转换

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

此强制转换处理某些不相关类型之间的转换,例如从一个指针类型到另一个不兼容的指针类型。它只会执行数据的二进制复制,而不会更改基础位模式。请注意,这种低级操作的结果是特定于系统的,因此不可移植。如果不能完全避免,则应谨慎使用。

动态投射

此类型仅用于将对象指针和对象引用转换为继承层次结构中的其他指针或引用类型。它是唯一确保指向的对象可以转换的强制转换方法,方法是执行运行时检查指针是否引用了目标类型的完整对象。要使此运行时检查成为可能,对象必须是多态的。也就是说,该类必须定义或继承至少一个虚函数。这是因为编译器将仅为此类对象生成所需的运行时类型信息。

动态转换示例

在下面的示例中,使用动态强制转换将指针转换为指针。此派生到基的转换成功,因为 Child 对象包含一个完整的 Base 对象。MyChildMyBase

class MyBase 
{ 
  public:
  virtual void test() {}
};
class MyChild : public MyBase {};



int main()
{
  MyChild *child = new MyChild();
  MyBase  *base = dynamic_cast<MyBase*>(child); // ok
}

下一个示例尝试将指针转换为指针。由于 Base 对象不包含完整的 Child 对象,因此此指针转换将失败。为了指示这一点,动态强制转换返回一个 null 指针。这提供了一种方便的方法来检查转换在运行时是否成功。MyBaseMyChild

MyBase  *base = new MyBase();
MyChild *child = dynamic_cast<MyChild*>(base);

 
if (child == 0) 
std::cout << "Null pointer returned";

如果转换的是引用而不是指针,则动态强制转换将因引发异常而失败。这需要使用语句进行处理。bad_casttry-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

但是,在第二个示例中,转换可能会导致运行时错误。如果对象包含实例,则会发生错误,但如果它包含实例,则不会发生错误。在某些情况下,这可能要到运行时才能知道。在这种情况下,动态强制转换是比静态强制转换更好的选择。MyBaseMyBaseMyChild

// 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

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

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

*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

来源和更多解释

评论

0赞 Evg 4/18/2017
std::bad_cast定义于<typeinfo>
2赞 Isidoro Ghezzi 9/7/2018
从子级到基层,不需要铸造:MyBase *base = child; // ok
7赞 Mohammed Noureldin 7/22/2019
在我看来,最好的答案,非常简单而清晰
4赞 Jazzy 5/5/2021
这真的应该是IMO的最佳答案
0赞 Olga Pshenichnikova 6/8/2022
这是最好的答案。