为什么使用static_cast<int>(x)而不是(int)x?

我听说static_cast函数应该优先于C风格或简单函数风格的铸造。这是真的吗?为什么?

409395 次浏览

除了操纵指向类的指针之外,static_cast还可用于执行类中显式定义的转换,以及在基本类型之间执行标准转换:

double d = 3.14159265;int    i = static_cast<int>(d);

static_cast意味着你不能意外地const_castreinterpret_cast,这是一件好事。

这是关于你想强加多少类型安全。

当您编写(bar) foo(如果您没有提供类型转换运算符,则等效于reinterpret_cast<bar> foo)时,您正在告诉编译器忽略类型安全,并按照它的要求执行。

当您编写static_cast<bar> foo时,您要求编译器至少检查类型转换是否有意义,并且对于整型类型,插入一些转换代码。


编辑2014-02-26

我在五年前写了这个答案,但我错了。(见评论。)但它仍然得到了好评!

一个实用的提示:如果您打算整理项目,您可以轻松地在源代码中搜索static_cast关键字。

主要原因是经典的C转换没有区分我们所谓的static_cast<>()reinterpret_cast<>()const_cast<>()dynamic_cast<>()。这四件事是完全不同的。

static_cast<>()通常是安全的。语言中有一个有效的转换,或者一个适当的构造函数使其成为可能。唯一有点风险的时候是当你向下转换到一个继承的类时;你必须通过语言外部的手段(比如对象中的标志)确保该对象实际上是你声称的后代。只要检查结果(指针)或考虑到可能的异常(引用),dynamic_cast<>()是安全的。

另一方面,reinterpret_cast<>()(或const_cast<>())总是危险的。你告诉编译器:“相信我:我知道这看起来不像foo(这看起来好像不是可变的),但它是”。

第一个问题是,如果不查看大量分散的代码并了解所有规则,几乎不可能判断C风格转换中会发生哪一个。

让我们假设这些:

class CDerivedClass : public CMyBase {...};class CMyOtherStuff {...} ;
CMyBase  *pSomething; // filled somewhere

现在,这两个是以相同的方式编译的:

CDerivedClass *pMyObject;pMyObject = static_cast<CDerivedClass*>(pSomething); // Safe; as long as we checked
pMyObject = (CDerivedClass*)(pSomething); // Same as static_cast<>// Safe; as long as we checked// but harder to read

但是,让我们看看这个几乎相同的代码:

CMyOtherStuff *pOther;pOther = static_cast<CMyOtherStuff*>(pSomething); // Compiler error: Can't convert
pOther = (CMyOtherStuff*)(pSomething);            // No compiler error.// Same as reinterpret_cast<>// and it's wrong!!!

正如您所看到的,如果不了解所涉及的所有类,就没有简单的方法来区分这两种情况。

第二个问题是C风格的强制转换太难定位。在复杂的表达式中,很难看到C风格的强制转换。如果没有成熟的C++编译器前端,几乎不可能编写一个需要定位C风格强制转换的自动化工具(例如搜索工具)。另一方面,搜索“static_cast<”或“reinterpret_cast<”很容易。

pOther = reinterpret_cast<CMyOtherStuff*>(pSomething);// No compiler error.// but the presence of a reinterpret_cast<> is// like a Siren with Red Flashing Lights in your code.// The mere typing of it should cause you to feel VERY uncomfortable.

这意味着,不仅C风格的转换更危险,而且更难找到它们以确保它们是正确的。

这个问题不仅仅是使用static_cast<>还是C风格的转换,因为使用C风格的转换时会发生不同的事情。C++转换运算符旨在使这些不同的操作更加明确。

从表面上看,static_cast<>和C风格的转换似乎是一样的,例如,当将一个值转换为另一个值时:

int i;double d = (double)i;                  //C-style castdouble d2 = static_cast<double>( i );  //C++ cast

这两种方法都将整数值转换为双精度。然而,当使用指针时,事情变得更加复杂。一些例子:

class A {};class B : public A {};
A* a = new B;B* b = (B*)a;                                  //(1) what is this supposed to do?
char* c = (char*)new int( 5 );                 //(2) that weird?char* c1 = static_cast<char*>( new int( 5 ) ); //(3) compile time error

在这个例子中,(1)可能是OK的,因为A指向的对象实际上是B的一个实例。但是,如果您在代码中不知道a实际上指向什么呢?

(2)可能是完全合法的(您只想查看整数的一个字节),但它也可能是一个错误,在这种情况下,错误会很好,例如(3)。

C++转换运算符旨在通过尽可能提供编译时或运行时错误来在代码中暴露这些问题。

因此,对于严格的“值转换”,您可以使用static_cast<>。如果您想在运行时对指针进行多态转换,请使用dynamic_cast<>。如果您真的想忘记类型,您可以使用reintrepret_cast<>。只需将const抛出窗口,还有const_cast<>

他们只是让代码更明确,这样看起来你知道你在做什么。

在代码块中很容易忽略C样式转换。C++样式转换不仅是更好的实践;它们提供了更大程度的灵活性。

reinterpret_cast允许积分到指针类型转换,但是如果滥用可能是不安全的。

static_cast为数字类型提供了良好的转换,例如从枚举到整数或整数到浮点数或您确信类型的任何数据类型。它不执行任何运行时检查。

另一方面,dynamic_cast将执行这些检查,标记任何不明确的赋值或转换。它只适用于指针和引用,并产生开销。

还有其他几个,但这些是你会遇到的主要问题。

  1. 允许在您的代码使用grep或类似工具。
  2. 使它明确什么样的你正在做的演员,而且很吸引人编译器在强制执行它时的帮助。如果你只想抛弃const-ness,然后您可以使用const_cast,这不会让你进行其他类型的转换。
  3. 演员天生丑陋-你作为程序员正在推翻如何编译器通常会将您的代码。你是在说编译器,“我比你更清楚。”既然如此,那就说得通了表演演员应该是一个适度痛苦的事情,和他们应该在你的代码,因为它们是可能的来源的问题。

有效C++介绍

总之

  1. static_cast<>()给你一个编译时检查能力,C风格卡斯特没有。
  2. static_cast<>()很容易被发现C++源代码中的任何地方;相比之下,C_Style更难发现。
  3. 使用C++可以更好地传达意图。

更多解释

静态转换执行兼容类型之间的转换。它类似于C样式转换,但更具限制性。例如,C样式转换将允许整数指针指向char。

char c = 10;       // 1 byteint *p = (int*)&c; // 4 bytes

因为这会导致一个4字节的指针指向分配的1字节内存中,写入此指针将导致运行时错误或将覆盖一些相邻的内存。

*p = 5; // run-time error: stack corruption

与C风格的强制转换相反,静态强制转换将允许编译器检查指针和指针数据类型是否为兼容,这允许程序员捕捉到这种不正确的情况编译期间的指针赋值。

int *q = static_cast<int*>(&c); // compile-time error

阅读更多:
static_cast<>和C风格的铸造有什么区别

常规演员对static_cast对dynamic_cast