我听说static_cast函数应该优先于C样式或简单的函数样式转换。这是真的吗?为什么?
- 反对法官大人,问了又答。
- 我不同意,这另一个问题是描述C++中的Casts介绍之间的差异。这个问题是关于静态演员表的真正有用性,这略有不同。
- 我们当然可以合并这两个问题,但是我们需要保留的是,在这个线程中使用函数比使用C样式的强制转换的优势,而C样式的强制转换目前只在另一个线程的一行回答中提到,没有投票权。
- 向上投票,因为该链接仍然提供有用的信息。
- 这个问题是关于"内置"类型的,比如int,而这个问题是关于类类型的。这似乎是一个重要的差异,值得单独解释。
- 静态类型转换实际上是一个运算符,而不是一个函数。
- @thomasmcleod运算符是一种特殊的函数类型(至少我是这么想的)
- @darthrubik,实际上函数调用是一种运算符。en.wikipedia.org/wiki/operators_in_c_and_c%2b%2b
主要原因是经典的C型铸造没有区别于我们称之为static_cast<>()、reinterpret_cast<>()、const_cast<>()和dynamic_cast<>()。这四件事完全不同。
static_cast<>()通常是安全的。语言中有一个有效的转换,或者一个适当的构造函数使其成为可能。唯一有点危险的时候是当您将对象下放到继承的类中时;您必须通过语言外部的方式(如对象中的标志),确保对象实际上是您声称的后代。只要检查结果(指针)或考虑到可能的异常(参考),dynamic_cast<>()是安全的。
另一方面,reinterpret_cast<>()或const_cast<>()总是危险的。你告诉编译器:"相信我:我知道这看起来不像一个foo,但它是可变的。"
第一个问题是,如果不看大而分散的代码片段,不知道所有规则,几乎不可能知道哪一个将出现在C样式的演员表中。
假设如下:
1 2 3 4
| class CDerivedClass : public CMyBase {...};
class CMyOtherStuff {...} ;
CMyBase *pSomething; // filled somewhere |
号
现在,这两者的编译方式相同:
1 2 3 4 5 6
| 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 |
但是,让我们看看这个几乎相同的代码:
1 2 3 4 5 6
| 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<"。
1 2 3 4 5
| 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来构建继承层次,而应该使用dynamic_cast。它将返回空指针或有效指针。
- @大卫·索恩利:我同意,通常。我想我指出了在那种情况下使用static_cast的注意事项。dynamic_cast可能更安全,但并不总是最佳选择。有时,您确实知道指针指向给定的子类型,这对编译器来说是不透明的,而static_cast更快。至少在某些环境中,dynamic_cast需要可选的编译器支持和运行时成本(启用rtti),您可能不希望仅为几个检查而启用它。C++的RTTI只是解决这个问题的一种可能方法。
- 你关于C型铸造的说法是错误的。所有的C转换都是值转换,大致相当于C++ EDCOX1〔7〕。reinterpret_cast的c等价物是*(destination_type *)&,即取对象的地址,将该地址转换为指向不同类型的指针,然后取消引用。除了字符类型或特定结构类型(C为其定义了该构造的行为)之外,它通常会导致C中的未定义行为。
- 你的回答很好,说明了文章的主题。我在寻找标题"为什么使用static_cast(int)(x)而不是(int)x"的答案。也就是说,对于类型int和int,为什么使用static_cast和(int)作为唯一的好处似乎是类变量和指针。请你详细说明一下。
- chux intdynamic_cast",是不是适用于所有的,但其他原因的立场。例如:让我们说是一个函数的参数vfloat(int)v是符合的,然后static_cast(v)。如果你float*参数变化对(int)v别出声,而成为reinterpret_cast(v)static_cast(v)是非法和正确地捕获由编译器。
- "我是一bitten euromicelli:你描述的情况。我到的时候,我int铸造double*double被认为它是。它把我这一天半的调试,因为当时的风格,避免casts I C的所有成本。
- 你所有的类的实例是一个非常好的和适当的解释,为什么美国要低的工作coders铸造算子使用C + +。《我有一个问题是你的,当你正在处理一个POD型样或void void * * * *国际?在这样的情况我使用C或C + + casts铸造算子?我会在这个问题上的终端在不同的岗位。不知道在这所属。
- 当它是不清楚是否应该的问题是关于C + +风格的casts在一般或特别是关于算术casts,奇怪的是,它给你的问题,这似乎是一个例子的算法的一个铸造,而完全忽略casts算术和接受的答案focuses专门在线casts层次指针。(?????????????????????)
- 其实这是一个可以提高C++风格的casts应保留的层次和不同的指针casts casts砍,而经典的C风格的casts应用于casts算法。这将提供明确的区分这两个(或三个)之间的不同的世界,而不是casts)。在其他词,当你是一个演员表演的算法,实际上是对int,(int) xThe Way To Go,不static_cast(x)。
- 有一种之间的差异andreyt @ C风格的铸造和static_cast……const部分(即,如果我们谈的casts算法),这是不相关的,但在C是在C + +编译器的执行是由很多更多……未定义的行为和可以导致如果你不仔细。
- 你可能是正确的andreyt"技术。然而,使用C + +风格的情况下,增加的检测casts程序员编译器的错误(例如,使用accidentally铸造static_cast<>是二手,但reinterpret_cast<>是必需的)。没有真正的理由是通上。这包括与在铸造,以(int),因为你不想在你的应用程序到silently reinterpret_castrefactored一些代码)。
- rubenvb:"不是我不明白,你在谈论什么。你必须confusing什么的。的问题,const不进入算术casts播放中.的问题,const只适用于casts /参考指针,这是不算数的。所以,你想做点什么和如何利用它来提const相关我在对我说是不明确的。
- 看:"如果它是主要的操作系统可重构算法将转向一浇入铸,那么它将需要的代码审查meticulous无论如何,这会涉及到很多这样的检测与校正的多casts(assuming就是有一个可信的用例)。
- 该效应的附加件提供这样的心智中是微不足道的static_cast案例,在我看来,相对于正效应提供更优雅的代码readability casts视觉算法和分层算法casts)从分离casts casts /黑客。我相信后者是显着更多的有价值的影响。所以我坚持与C风格的转换:在算术casts远远远outweighs回报的风险。
- "那是andreyt和点代码审查可以很容易地meticulous小姐是因为缺乏这些东西,他们的意图是不verbosity unambiguously说。这会导致错误。
- @瑟:哦,当重构如此激烈的时候,一个细致的代码审查会遗漏很多事情,其中这个具体的问题根本不是一个大因素。回到主题:换句话说,尽管他们想分开不同类型的演员,但他们做得还不够。static_cast仍然太拥挤。我需要一个算术转换专用的演员表。我非常需要它,所以我会接受使用C样式的强制转换进行算术转换的风险,只是让我的算术强制转换看起来不同。
- @andreyt没有什么能阻止你做像integer_cast<>这样的事情,它可以做你需要做的任何事情(我认为在boost integer中可能有这样的东西)。类似的事情可能已经存在(有专门的libs,如safeint)
- you must make sure that the object is a actually the descendant that you claim it is, by means external to the language (like a flag in the object).我不同意(除了对象中的成员不是"语言外部的"):如果程序设计得当,就完全有可能知道对象是正确的类型,并且标记它会浪费空间。只要确保您知道自己的实例是什么,如果您是static_casting,并且在运行调试构建时确保在等效dynamic_cast上执行assert()。
- @JTerm什么?如果被强制转换为"child"的实例是一个孩子,并且确实有这些成员,那么就没有什么不安全的了,这是一个好的程序员会知道的。(理想情况下,调试版本中的assert( dynamic_cast ... ))
- @下划线"d",通过"外部到语言",我的意思是,语言不会强制执行任何此类标志的含义(编译器不知道它的含义,也无法检查它)。我知道我在做什么,"正如你所指出的,这是完全有效的,当然,可能会避免你使用一个标志,这取决于程序。这也是"语言外部的":-)。
- "我们称之为static>()、reinterpret>()、const>()和dynamic>"的区别是没有通过c样式转换或函数样式转换语法完成的dynamic_cast<>。
- dynamic_cast<>()与C型铸造无关。
- static_cast可能有风险的另一种情况是,static_cast允许您将任何指针类型转换为void*,void*转换为任何指针类型,例如,可以将char*转换为void*然后转换为toint*。
- 使用宏定义的关键字定位跨C文件的强制转换是一个好的实践。
一个实用的提示:如果你打算整理项目,你可以很容易地在源代码中搜索静态的强制转换关键字。
- 您也可以使用括号搜索,例如"(int)",但是使用C++风格的转换的好答案和有效的理由。
- @Mike会发现误报-一个带有单个int参数的函数声明。
- 这可能会产生错误的否定:如果您搜索的代码库不是唯一的作者,您将不会发现其他人可能出于某些原因引入的C样式的强制转换。
- 这样做有助于清理项目吗?
In short:
static_cast<>() gives you a compile time checking ability, C-Style
cast doesn't.
static_cast<>() can be spotted easily
anywhere inside a C++ source code; in contrast, C_Style cast is harder to spot.
Intentions are conveyed much better using C++ casts.
More Explanation:
The static cast performs conversions between compatible types. It
is similar to the C-style cast, but is more restrictive. For example,
the C-style cast would allow an integer pointer to point to a char.
1 2
| char c = 10; // 1 byte
int *p = (int*)&c; // 4 bytes |
Since this results in a 4-byte pointer pointing to 1 byte of allocated
memory, writing to this pointer will either cause a run-time error or
will overwrite some adjacent memory.
1
| *p = 5; // run-time error: stack corruption |
In contrast to the C-style cast, the static cast will allow the
compiler to check that the pointer and pointee data types are
compatible, which allows the programmer to catch this incorrect
pointer assignment during compilation.
1
| int *q = static_cast<int*>(&c); // compile-time error |
号
阅读更多信息:静态铸造和C型铸造的区别是什么?和常规铸造vs.静态铸造vs.动态铸造
- 我不同意static_cast<>()的可读性更强。我的意思是,有时是这样,但大多数时候——特别是在基本整数类型上——它只是可怕的、不必要的冗长。例如:这是一个交换32位字字节的函数。使用static_cast()类型转换几乎不可能读取,但使用(uint##)类型转换很容易理解。代码图片:imgur.com/nohbgve
- @托德莱曼:谢谢,但我也没说"以东王"〔19〕。(但大多数时候是这样的)确实有些情况下,C样式的转换更易于阅读。这就是C型铸造仍然是生存和踢球的原因之一。:)顺便说一下,这是一个很好的例子。
- @该图像中的toddlehman代码使用两个已链接的强制转换((uint32_t)(uint8_t)来实现最低字节之外的字节被重置。因此,有位与(0xFF &)。使用强制转换使意图模糊。
这个问题比仅仅使用威瑟静态类型转换或C类型转换要严重,因为使用C类型转换时会发生不同的事情。C++铸造操作符旨在使这些操作更加显式。
在表面上,静态类型转换和C样式转换看起来是相同的,例如,将一个值转换为另一个值时:
1 2 3
| int i;
double d = (double)i; //C-style cast
double d2 = static_cast<double>( i ); //C++ cast |
这两种方法都将整数值强制转换为double。但是,当使用指针时,事情会变得更复杂。一些例子:
1 2 3 4 5 6 7 8
| 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的一个实例。但是,如果您在代码中不知道a实际指向的是什么呢?(2)也许完全合法(您只想查看整数的一个字节),但在这种情况下,它也可能是一个错误,在这种情况下,错误会很好,如(3)。C++铸造运算符旨在通过提供编译时间或运行时错误在可能的情况下在代码中公开这些问题。
因此,对于严格的"值强制转换",可以使用静态强制转换。如果要对指针进行运行时多态强制转换,请使用动态强制转换。如果你真的想忘记类型,你可以使用reintrepret-cast。把警察扔出窗外就是警察。
它们只是使代码更显式,这样看起来您就知道自己在做什么了。
static_cast是指你不能不小心的const_cast或reinterpret_cast这是件好事。
- 与C样式的演员表相比,它的其他(虽然很小)优势在于它更突出(做一些可能不好的事情应该看起来很难看),而且更出色。
- 在我的书中,优秀的能力总是一个优势。
允许轻松找到铸件使用grep或类似代码工具。
明确什么类型你正在做的演员,和迷人的编译器帮助实现它。如果你只想抛弃康斯,那你可以用警察局,不允许你进行其他类型的转换。
演员表天生就很难看——你就像一个程序员正在否决编译器通常会处理代码。你是在说编译器:"我比你更了解。"既然如此,这是有道理的演出应该是做一件中度疼痛的事情,以及他们应该在你的代码,因为它们可能是源代码问题。
见有效C++介绍
- 我完全同意这一类,但使用C++风格的吊舱类型有任何意义吗?
- 我认为是这样。所有3个原因都适用于pods,并且只有一个规则,而不是类和pods的单独规则,这很有帮助。
- 有趣的是,我可能需要在将来的pod类型代码中修改如何进行强制转换。
这是关于你想要强加多少类型的安全性。
当您编写(bar) foo时(如果您没有提供类型转换运算符,则相当于reinterpret_cast foo时),您告诉编译器忽略类型安全性,并按照它的指示进行操作。
当您编写static_cast foo时,您要求编译器至少检查类型转换是否有意义,对于整型,还需要插入一些转换代码。
编辑2014-02-26
5年前我写了这个答案,但我错了。(见评论)但它仍然得到了支持!
- (bar)foo不等于reinterpret_cast(foo)。"(类型)EXPR"的规则是,它将选择适当的C++风格的CAST来使用,这可能包括RealTytRCAST。
- 说得对。米切利给出了这个问题的最终答案。
C样式的强制转换在代码块中很容易丢失。C++样式转换不仅是更好的实践,还提供了更大程度的灵活性。
reinterpret_cast允许积分到指针类型的转换,但如果使用不当,则可能不安全。
静态类型转换为数值类型提供了良好的转换,例如从as-enum到ints或ints到float或任何您确信类型的数据类型。它不执行任何运行时检查。
另一方面,动态类型转换将执行这些检查,标记任何不明确的分配或转换。它只在指针和引用上工作,并产生开销。
还有一些其他的,但这些是你会遇到的主要的。
除了操作指向类的指针之外,静态类型转换还可以用于执行在类中显式定义的转换,以及在基本类型之间执行标准转换:
1 2
| double d = 3.14159265;
int i = static_cast<int>(d); |
- 但是,当(int)d更加简洁易读时,为什么会有人写static_cast(d)?(我的意思是在基本类型的情况下,不是对象指针。)
- @Toddlehman:一致性
- @为什么会有人把一致性放在可读性之上?(实际上是半认真的)
- @托德莱曼:我,考虑到对某些类型例外,仅仅因为它们对你有某种特殊性,对我没有任何意义,我也不同意你对可读性的看法。正如我在另一篇评论中看到的,越短并不意味着可读性就越高。
- 静态投射是一个明确而有意识的决定,以做出一种非常特殊的转换。因此,它增加了意图的清晰度。在代码审查、bug或升级练习中,作为搜索源文件以进行转换的标记,它也非常方便。
- @托德莱曼反驳道:为什么有人会在int{d}可读性更强的时候写(int)d?构造器,或者类似于()的函数,语法在复杂表达式中的圆括号中的转换速度并不是很快。在这种情况下,应该是int i{d},而不是int i = (int)d。在我看来,当我在表达式中只需要一个临时变量时,我使用static_cast,并且从未使用过构造函数强制转换,我不认为。我只在匆匆忙忙地写debug couts时使用(C)casts。