我听说static_cast
函数应该优先于 C 风格或简单的函数风格转换。这是真的?为什么?
9 回答
主要原因是经典的 C 类型转换不区分我们所说static_cast<>()
的 , reinterpret_cast<>()
,const_cast<>()
和dynamic_cast<>()
。这四件事是完全不同的。
Astatic_cast<>()
通常是安全的。语言中有一个有效的转换,或者一个适当的构造函数使它成为可能。唯一有点冒险的时候是当你放弃继承的类时;您必须通过语言外部的方式(如对象中的标志)确保该对象实际上是您声称它的后代。dynamic_cast<>()
只要检查结果(指针)或考虑可能的异常(参考),A就是安全的。
另一方面,A reinterpret_cast<>()
(或 a )总是危险的。const_cast<>()
你告诉编译器:“相信我:我知道这看起来不像 a 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 关键字。
简而言之:
static_cast<>()
为您提供编译时检查能力,C-Style cast 没有。static_cast<>()
可以在 C++ 源代码中的任何位置轻松发现;相比之下,C_Style 演员更难发现。- 使用 C++ 强制转换可以更好地传达意图。
更多解释:
静态转换执行兼容类型之间的转换。它类似于 C 风格的演员表,但更具限制性。例如,C 风格的转换将允许一个整数指针指向一个字符。
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
阅读更多内容: static_cast<> 和 C 风格转换
以及常规转换与 static_cast 与 dynamic_cast
有什么区别
问题不仅仅是使用 wither 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 的一个实例。但是如果你在代码中不知道 a 实际指向的那个点怎么办?(2) 可能完全合法(您只想查看整数的一个字节),但也可能是错误,在这种情况下,错误会很好,例如 (3)。C++ 强制转换运算符旨在通过在可能的情况下提供编译时或运行时错误来暴露代码中的这些问题。
因此,对于严格的“值转换”,您可以使用 static_cast。如果您想要指针的运行时多态转换,请使用 dynamic_cast。如果你真的想忘记类型,你可以使用 reintrepret_cast。并且只是将 const 扔出窗外,有 const_cast。
他们只是让代码更明确,这样看起来你知道自己在做什么。
static_cast
意味着你不能意外地const_cast
or reinterpret_cast
,这是一件好事。
- 允许使用 grep 或类似工具在您的代码中轻松找到强制转换。
- 明确说明您正在执行哪种类型的转换,并让编译器帮助执行它。如果你只想抛弃 const-ness,那么你可以使用 const_cast,它不允许你做其他类型的转换。
- 强制转换本质上是丑陋的——作为程序员,您正在推翻编译器通常如何处理您的代码。你是在对编译器说:“我比你更清楚。” 在这种情况下,执行强制转换应该是一件适度痛苦的事情,并且它们应该在您的代码中突出显示,因为它们可能是问题的根源。
请参阅有效的 C++简介
这是关于你想强加多少类型安全。
当您编写时(bar) foo
(相当于reinterpret_cast<bar> foo
您没有提供类型转换运算符),您是在告诉编译器忽略类型安全,并按照它的指示去做。
当您编写时,static_cast<bar> foo
您要求编译器至少检查类型转换是否有意义,并且对于整数类型,插入一些转换代码。
编辑 2014-02-26
我在 5 多年前写了这个答案,但我弄错了。(见评论。)但它仍然得到了赞成!
在代码块中很容易错过 C 风格转换。C++ 风格转换不仅是更好的实践;它们提供了更大程度的灵活性。
reinterpret_cast 允许整数到指针类型的转换,但是如果误用可能是不安全的。
static_cast 为数字类型提供了良好的转换,例如从枚举到整数或整数到浮点数或您确信类型的任何数据类型。它不执行任何运行时检查。
另一方面,dynamic_cast 将执行这些检查,标记任何不明确的分配或转换。它仅适用于指针和引用并产生开销。
还有其他几个,但这些是您会遇到的主要内容。
除了操作指向类的指针之外,static_cast 还可用于执行类中显式定义的转换,以及执行基本类型之间的标准转换:
double d = 3.14159265;
int i = static_cast<int>(d);