为什么要使用static_cast(x)而不是(int)x?
我听说static_cast
函数应该比C样式或者简单的函数样式转换更可取。这是真的?为什么?
解决方案
除了处理指向类的指针之外,static_cast还可以用于执行在类中显式定义的转换,以及在基本类型之间执行标准转换:
double d = 3.14159265; int i = static_cast<int>(d);
" static_cast"意味着我们不能偶然地使用" const_cast"或者" reinterpret_cast",这是一件好事。
这是关于我们要强加多少类型安全性。
当我们编写(bar)foo
(相当于reinterpret_cast <bar> foo
如果没有提供类型转换操作符)时,我们是在告诉编译器忽略类型安全性,并按照提示进行操作。
在编写" static_cast <bar> foo"时,我们要求编译器至少检查类型转换是否有意义,对于整数类型,请插入一些转换代码。
编辑2014-02-26
我在5年前就写了这个答案,但我弄错了。 (请参阅评论。)但是它仍然会被投票!
一个实用的技巧:如果我们打算整理项目,则可以在源代码中轻松搜索static_cast关键字。
主要原因是经典C强制转换在我们称为" static_cast <>()"," reinterpret_cast <>()"," const_cast <>()"和" dynamic_cast <>()"之间没有区别。这四件事完全不同。
static_cast <>()
通常是安全的。语言中存在有效的转换,或者使之成为可能的适当的构造函数。唯一有风险的是当我们降级为继承的类时。我们必须通过语言外部的方式(例如对象中的标记)确保该对象实际上是我们声称的对象的后代。只要检查结果(指针)或者考虑到可能的异常情况(参考)," dynamic_cast <>()"是安全的。
另一方面,reinterpret_cast <>()
(或者const_cast <>()
)总是很危险的。我们告诉编译器:"相信我:我知道这看起来不像foo
(看起来好像是不可变的),但是确实如此。"
第一个问题是,在不查看大量分散代码并知道所有规则的情况下,几乎不可能分辨出将在C样式转换中发生哪一个。
让我们假设这些:
class CMyClass : public CMyBase {...}; class CMyOtherStuff {...} ; CMyBase *pSomething; // filled somewhere
现在,这两种编译方式相同:
CMyClass *pMyObject; pMyObject = static_cast<CMyClass*>(pSomething); // Safe; as long as we checked pMyObject = (CMyClass*)(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 cast double 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)可能是正确的,因为A所指向的对象实际上是B的实例。但是,如果我们在那时不知道代码实际指向什么,该怎么办? (2)也许是完全合法的(我们只想查看整数的一个字节),但也可能是一个错误,在这种情况下,一个错误会很好,例如(3)。 C ++强制转换运算符旨在通过在可能时提供编译时或者运行时错误来在代码中暴露这些问题。
因此,对于严格的"价值转换",可以使用static_cast。如果要在运行时对指针进行多态转换,请使用dynamic_cast。如果我们真的想忘记类型,可以使用reintrepret_cast。要将const抛出窗口,可以使用const_cast。
它们只是使代码更加明确,使我们看起来好像知道自己在做什么。
C样式强制转换很容易在代码块中丢失。 C ++样式转换不仅是更好的做法,它们提供了更大的灵活性。
reinterpret_cast允许整数到指针类型的转换,但是如果使用不当,可能是不安全的。
static_cast可为数字类型(例如)提供良好的转换从as枚举到int或者从int到float或者我们确信类型的任何数据类型。它不执行任何运行时检查。
另一方面,dynamic_cast将执行这些检查,标记所有不明确的分配或者转换。它仅适用于指针和引用,并且会产生开销。
还有其他一些,但是这些是我们会遇到的主要问题。
- 使用grep或者类似工具,可以轻松在代码中找到强制类型转换。
- 明确说明我们正在执行哪种类型的强制转换,并请编译器帮助执行该强制转换。如果只想放弃常量性,则可以使用const_cast,它将不允许我们进行其他类型的转换。
- 强制转换本质上是丑陋的-作为程序员,我们正在否定编译器通常如何对待代码。我们对编译器说:"我比我们了解得更多。"在这种情况下,执行强制转换应该是一件很痛苦的事情,并且应该在代码中坚持下去,这是有道理的,因为它们很可能会引起问题。
请参阅有效的C ++简介