常规演员与static_cast与dynamic_cast

Graeme Perrow 1570 c++ pointers casting

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

MyClass *m = (MyClass *)ptr;

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

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

Johannes Sch.. 1477

的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,并且还没有任何铸造,作为一个"向上铸造"是一个隐式转换.

常规演员

这些演员也被称为C型演员.C风格的演员与尝试一系列C++演员序列基本相同,并且在没有考虑的情况下采用第一个可行的C++演员dynamic_cast.不用说,这是更强大,因为它结合了所有的const_cast,static_castreinterpret_cast,但它也是不安全的,因为它不使用dynamic_cast.

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

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

  • 另请参阅boost的另外两个演员阵容:http://www.boost.org/doc/libs/1_47_0/libs/conversion/cast.htm#Polymorphic_cast (8认同)
  • 你能更详细地解释为什么动态演员表中的向下转换无效吗?假设`Derived`有一个我想要达到的`成员m',如果`dynamic_cast`不是一个选项,它将如何实现? (3认同)
  • @ JohannesSchaub-litb:你确定C风格的演员会让你"安全地"演绎到一个私人基类吗?我可以看到,当私有基类是唯一的/ base /时,工作,但虚拟/多重继承呢?我假设C样式转换没有指针操作. (2认同)

Breeze.. 142

静态演员

静态强制转换执行兼容类型之间的转换.它类似于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 cast主要是在有一个带有非常量指针参数的函数时使用,即使它没有修改指针.

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

然后可以使用const转换将函数传递给常量变量.

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

print(nonConst); // allowed

来源和更多解释


TJ Seabrooks.. 75

你应该看一下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异常.当它没有失败时,动态强制转换将目标类型的指针或引用返回给表达式引用的对象.

reinterpret_cast的

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


Jason Baker.. 26

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

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

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

  • ^是的,因为明确标记并故意局限于明确定义的角色的C++演员比C演员更"地狱",只是盲目地尝试多种类型的演员直到_anything_工作,无论感觉如何......好的. (12认同)
  • Bjarne Stroustrup ......那个给我们这个类型铸造地狱的人呢? (7认同)

ugasoft.. 25

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

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


DrPizza.. 12

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

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


Inisheer.. 10

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


larsmoa.. 10

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

其他答案中涵盖了C风格(和其他)演员阵容.


归档时间:

查看次数:

623053 次

最近记录:

2 年,4 月 前