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

Tom*_*ert 628 c++ casting

我听说该static_cast函数应该优先于C风格或简单的函数式转换.这是真的?为什么?

Eur*_*lli 608

主要的原因是,经典的C蒙上之间做出我们所说的没有什么区别static_cast<>(),reinterpret_cast<>(),const_cast<>(),和dynamic_cast<>().这四件事完全不同.

A static_cast<>()通常是安全的.语言中有一个有效的转换,或者使它成为可能的适当构造函数.唯一一次有点危险的是当你被归入一个继承的阶级时; 您必须通过语言外部的方式(如对象中的标志)确保该对象实际上是您声称它的后代.dynamic_cast<>()只要检查结果(指针)或考虑可能的异常(参考),A 就是安全的.

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

第一个问题是,几乎不可能在没有大量分散代码和了解所有规则的情况下分辨C风格的演员阵容.

我们假设这些:

class CDerivedClass : public CMyBase {...};
class CMyOtherStuff {...} ;

CMyBase  *pSomething; // filled somewhere
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

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

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!!!
Run Code Online (Sandbox Code Playgroud)

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

第二个问题是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.
Run Code Online (Sandbox Code Playgroud)

这意味着,不仅C风格的演员阵容更加危险,而且要找到它们以确保它们是正确的更难.

  • @David Thornley:我同意,通常.我想在那种情况下我指出了使用`static_cast`的注意事项.`dynamic_cast`可能更安全,但并不总是最好的选择.有时你通过对编译器不透明的方式*知道*指针指向给定的子类型,并且`static_cast`更快.在至少某些环境中,`dynamic_cast`需要可选的编译器支持和运行时成本(启用RTTI),您可能不希望仅为自己可以进行的几项检查启用它.C++的RTTI只是该问题的一种可能解决方案. (41认同)
  • 你不应该使用`static_cast`来强制转换继承层次结构,而应该使用`dynamic_cast`.这将返回空指针或有效指针. (29认同)
  • @ chux,对于`int``cynamic_cast`不适用,但所有其他原因都有效.例如:假设`v`是一个声明为`float`的函数参数,那么`(int)v`是`static_cast <int>(v)`.但是如果你将参数改为`float*`,那么`(int)v`就会变成`reinterpret_cast <int>(v)`而``static_cast <int>(v)`是非法的,并被编译器正确捕获. (29认同)
  • 你对C演员的主张是错误的.所有C类型转换都是值转换,大致与C++`static_cast`相当.`reinterpret_cast`的C等价物是`*(destination_type*)&`,即获取对象的地址,将该地址转换为指向不同类型的指针,然后解除引用.除了C语言定义此构造行为的字符类型或某些结构类型的情况外,它通常会导致C中的未定义行为. (15认同)
  • 你的好答案解决了帖子的主体.我正在寻找标题"为什么使用static_cast <int>(x)而不是(int)x"的答案.也就是说,对于类型`int`(和单独的`int`),为什么使用`static_cast <int>`vs.(int)`作为唯一的好处似乎是类变量和指针.请求您详细说明. (11认同)
  • 虽然目前尚不清楚的问题,即是否应该是关于C++ - 风格一般蒙上或约算术特别蒙上,很奇怪地看到,这个问题用什么似乎是一个算术投作为一个例子,而接受的答案完全忽略算术强制转换并专注于分层指针强制转换.(???) (5认同)
  • @AndreyT并且重点是细致的代码审查很容易错过这些东西,正是因为它们缺乏冗长 - 意图没有明确说明.这会导致错误. (3认同)
  • @AndreyT从技术上讲,你可能是对的.但是,使用C++样式转换会增加编译器检测程序员意外使用错误转换的情况(例如,使用`static_cast <>`,但需要`reinterpret_cast <>`).没有理由放弃这一点.不,这包括强制转换为`(int)`,因为您不希望编译器在重构某些代码后静默应用`reinterpret_cast <int>`. (2认同)
  • @rubenvb:我不明白你在说什么。你一定是混淆了一些东西。`const` 的问题在算术强制转换中根本不起作用。`const` 的问题仅适用于指针/引用强制转换,这不是算术。所以,你在这里提到 `const` 的目的是什么,以及它与我上面所说的有什么关系,我不清楚。 (2认同)
  • @sehe:如果重构是如此重要以至于它可以将算术转换为重新解释演员,那么无论如何都需要进行细致的代码审查,这将涉及比检测和纠正这些演员更多(假设甚至可信)用例). (2认同)
  • 在我看来,与更优雅的算术强制转换以及算术强制转换与分层强制转换/黑客强制转换所提供的对代码可读性的积极影响相比,在这种情况下,“static_cast”提供的额外思想的影响微不足道. 我相信后一种效果明显更有价值。所以我坚持使用 C 风格的算术转换:回报远远超过风险。 (2认同)
  • @AndreyT没有什么可以阻止你做像“integer_cast &lt;&gt;”这样的事情,它会做你需要它做的任何事情(我认为Boost Integer中可能有一些东西可以做到这一点)。类似的东西可能已经存在(具有像[SafeInt](http://msdn.microsoft.com/en-us/library/dd570023.aspx)这样的专门库) (2认同)
  • dynamic_cast &lt;&gt;()与c样式转换无关。 (2认同)

Kar*_*arl 110

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

  • 如何做到这一点有助于整理项目? (7认同)
  • @Mike会发现误报 - 带有单个`int`参数的函数声明. (4认同)
  • 您也可以使用括号搜索,例如"(int)"但是使用C++样式转换的良好答案和正当理由. (3认同)

Rik*_*ika 73

简而言之:

  1. static_cast<>() 给你一个编译时检查能力,C-Style演员没有.
  2. static_cast<>()可以在C++源代码中的任何地方轻松发现; 相比之下,C_Style演员更难以发现.
  3. 使用C++强制转换可以更好地传达意图.

更多说明:

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

char c = 10;       // 1 byte
int *p = (int*)&c; // 4 bytes
Run Code Online (Sandbox Code Playgroud)

由于这会导致4字节指针指向已分配内存的1个字节,因此写入此指针将导致运行时错误或将覆盖某些相邻内存.

*p = 5; // run-time error: stack corruption
Run Code Online (Sandbox Code Playgroud)

与C样式转换相反,静态转换将允许编译器检查指针和指针数据类型是否兼容,这允许程序员在编译期间捕获这种不正确的指针赋值.

int *q = static_cast<int*>(&c); // compile-time error
Run Code Online (Sandbox Code Playgroud)

阅读更多内容:
static_cast <>和C样式转换
以及Regular cast与static_cast与dynamic_cast 之间的区别是什么

  • 我不同意`static_cast <>()`更具可读性.我的意思是,*有时*它是,但大部分时间 - 特别是在基本的整数类型 - 它只是可怕和不必要的冗长.例如:这是一个交换32位字的字节的函数.使用`static_cast <uint ##>()`强制转换几乎不可能读取,但使用`(uint ##)`强制转换很容易理解.**图片代码:**http://imgur.com/NoHbGve (19认同)
  • 该图像中的@ToddLehman代码使用链接的两个强制转换(`(uint32_t)(uint8_t)`)来实现除最低之外的字节重置.为此,有按位和(`0xFF&`).演员阵容的使用是模糊的意图. (7认同)
  • @ToddLehman:谢谢,但我也没说'总是'.(但大部分时间都是肯定的)确实存在c样式转换更具可读性的情况.这就是c风格演员在c ++ imho中仍然活着和踢的原因之一.:)顺便说一下,这是一个非常好的例子 (3认同)
  • @ToddLehman你的代码是为什么首先要避免强制转换的一个确切例子。(参见不带强制转换的替代实现http://fxr.watson.org/fxr/source/lib/libkern/bswap32.c?v=NETBSD3) (2认同)

Dus*_*ell 28

问题不仅仅是使用枯萎的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
Run Code Online (Sandbox Code Playgroud)

这两个都将整数值转换为double.然而,当使用指针时,事情变得更加复杂.一些例子:

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
Run Code Online (Sandbox Code Playgroud)

在这个例子中(1)也许没问题,因为A指向的对象实际上是B的一个实例.但是如果你在代码中不知道实际指向的是什么呢?(2)也许完全合法(你只想查看整数的一个字节),但它也可能是一个错误,在这种情况下错误会很好,如(3).C++转换运算符旨在通过在可能的情况下提供编译时或运行时错误来在代码中公开这些问题.

因此,对于严格的"值转换",您可以使用static_cast.如果要运行时多态转换指针,请使用dynamic_cast.如果您真的想忘记类型,可以使用reintrepret_cast.只是将const抛出窗口就有const_cast.

它们只是使代码更加清晰,因此看起来你知道自己在做什么.


DrP*_*zza 25

static_cast意味着你不能意外const_cast或者reinterpret_cast,这是一件好事.

  • 与C风格演员相比,额外(虽然相当轻微)的优势在于它更突出(做一些可能看起来很糟糕的事情应该看起来很丑陋)并且它更加强大. (4认同)
  • 在我的书中,grep-ability总是一个加分. (4认同)

Pit*_*rou 7

这是关于你想要施加多少类型安全性.

当你写(bar) foo(这相当于reinterpret_cast<bar> foo你没有提供类型转换操作符)时,你告诉编译器忽略类型安全,并按照它的说法去做.

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


编辑2014-02-26

我在5年多前写过这个答案,我弄错了.(见评论.)但它仍然得到了支持!

  • (bar)foo不等于reinterpret_cast <bar>(foo)."(TYPE)expr"的规则是它将选择要使用的适当C++样式,包括reinterpret_cast. (7认同)

Joh*_*McG 7

  1. 允许使用grep或类似工具在代码中轻松找到强制转换.
  2. 明确表示您正在做什么样的演员表,并让编译器帮助他们执行.如果你只想抛弃const-ness,那么你可以使用const_cast,它不允许你进行其他类型的转换.
  3. 转换本质上是丑陋的 - 你作为程序员正在推翻编译器通常如何处理你的代码.你是对编译器说的,"我比你更清楚." 在这种情况下,执行演员应该是一件中等难度的事情是有道理的,并且他们应该在你的代码中坚持下去,因为它们可能是问题的根源.

请参阅有效的C++简介


Kon*_*rad 6

C样式强制转换很容易在代码块中丢失。C ++样式转换不仅是更好的做法,它们提供了更大的灵活性。

reinterpret_cast允许整数到指针类型的转换,但是如果使用不当,可能是不安全的。

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

另一方面,dynamic_cast将执行这些检查,标记所有不明确的分配或转换。它仅适用于指针和引用,并且会产生开销。

还有其他一些,但是这些是您会遇到的主要问题。


pra*_*ash 5

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

double d = 3.14159265;
int    i = static_cast<int>(d);
Run Code Online (Sandbox Code Playgroud)

  • 但是,当 `(int)d` 更加简洁和可读时,为什么有人会写 `static_cast&lt;int&gt;(d)` 呢?(我的意思是在基本类型的情况下,而不是对象指针。) (7认同)
  • @ToddLehman:我,考虑到仅仅因为某些类型对你来说很特殊而对它们进行例外对我来说没有任何意义,而且我也不同意你的可读性概念。正如我从您在另一条评论中发布的图片中看到的那样,更短并不意味着更具可读性。 (2认同)
  • static_cast 是一个明确且有意识的决定,用于进行非常特殊的转换。因此,它增加了意图的清晰度。作为在代码审查、错误或升级练习中搜索源文件进行转换的标记,它也非常方便。 (2认同)