8.5 - 显式类型转换和static_cast
Key Takeaway
- C++支持 5 种类型的显示类型转换: C风格类型转换、静态类型转换、const 类型转换、动态类型转换和重新解释类型转换。后四种类型有时称为具名名类型转换(named cast)。
- C 语言类型的类型转换在不同的语境下会产生不同的效果,尽量不要使用
- 优先使用
static_cast
,它提供了运行时的类型检查机制,不容易犯错。 - 使用
static_cast
进行显式地缩窄转换
在 8.1 - 隐式类型转换中我们介绍过,编译器可以隐式地将一种类型的值转换成另外一种类型,即隐式类型转换。当你想要将一个数值类型通过数值提升的方式转换为更宽的类型时,使用隐式类型转换是可以的。
很多新手 C++ 程序员会这样做:
1 |
|
因为 10
和 4
都是 int
类型,所以执行的是整型除法,表达式的求值结果为 int
值 2
。然后,该值在被用于初始化变量 d
之前被转换成了 double
类型的 2.0
。 多数情况下,程序员并不会故意这么做。
下面这个例子中,我们使用了字面量,将上面的int
型操作数替换成了double
操作数,这样一来就会进行浮点数除法:
1 |
|
但是,如果你使用的是变量而非字面量呢?考虑下面的例子:
1 2 3 |
|
因为执行了整型除法,所以变量 d
的值最终为2.0
。那么我们应该如何告诉编译器,这里需要使用浮点数除法呢?字面量后缀并不能被用在变量上。因此,需要一种能够将变量转换为浮点类型的方法,以便使用浮点数除法。
幸运的是,C++提供了许多不同的类型转换操作符(通常称为类型转换),程序员可以使用它们请求编译器执行类型转换。因为类型转换是程序员的显式请求,所以这种形式的类型转换通常称为显式类型转换(与隐式类型转换相反,隐式类型转换是编译器自动执行的类型转换)。
类型转换
C++支持 5 种类型的显示类型转换: C风格类型转换、静态类型转换、const 类型转换、动态类型转换和重新解释类型转换。后四种类型有时称为具名名类型转换(named cast)。
在本课中,我们将介绍C风格类型转换和静态类型转换。
相关内容
我们会在18.10 -- Dynamic casting中介绍动态类型转换,但是我们首先要介绍一些前置内容。
通常应该避免使用const 类型转换和重新解释类型转换,因为只有很少的情况下会需要使用它们,而且使用不当是非常有害的。
注意
除非你有充分的理由,否则请避免使用const 类型转换和重新解释类型转换
C语言风格的类型转换
在标准的C语言中,强制转换是通过()
运算符完成的,括号内为需要转换的类型名。在C++中,你仍然可以在由C语言转换而来的代码中看到它们。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
在上面的程序中,我们使用了一个C语言风格的类型转换,要求编译器将x
转换为double
。因为,/
左侧的操作数被转换成了浮点数,所以操作符右侧的数同样也会被转换为浮点数(数值转换),然后表达式就会按照浮点数除法而不是整型除法求值!
C++ 允许你使用一种更加类似函数调用的语法来使用C语言风格的类型转换:
1 |
|
这种方式实现的类型转换和之前一种是完全一样的,但是将需要转换的变量放在括号了,更容易看清楚被转换的对象是什么。
虽然“c风格类型转换”看起来是单一类型转换,但实际上它可以根据上下文执行各种不同的转换。这可以包括“静态类型转换”、“const类型转换”或“重新解释类型转换”(我们在上面提到的后两种类型应该避免)。因此,“C风格强制转换”有可能被无意中误用,而不会产生预期的行为,而使用c++强制转换则可以避免这种情况。
相关内容
如果你很好奇,C语言风格的类型转换是如何工作的,可以参考这篇文章 。
最佳实践
避免使用C语言风格的类型转换。
static_cast
C++ 引入了一个新的强制转换运算符static_cast
,用于将一种类型的值转换为另外一种类型。
在之前的课中,你可能已经见识过如何使用 static_cast
将 char
转换为 int
使得 std::cout
可以打印整型而不是 char
:
1 2 3 4 5 6 7 8 9 |
|
static_cast
运算符将一个表达式作为输入,然后将表达式求值的结果转换为尖括号中指定的类型。static_cast
是将一种基础数据类型转换为另一种基础数据类型的最佳途径。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
static_cast
最大的优势是它提供了运行时的类型检查机制,这样就不容易犯下由粗心导致的问题。 static_cast
的功能不如 C 语言风格的类型转换(故意的)强大,所以你不会无意间移除const
或其他你本不希望发生的事情。
最佳实践
在需要进行类型转换时,优先使用 static_cast
。
使用 static_cast
进行显式地缩窄转换
当我们进行具有潜在危险的(缩窄)隐式类型转换时,编译器通常会发出告警。例如,考虑下面这段代码:
1 2 |
|
将int
(2 字节或者 4 字节) 转换为char
(1 字节)通常是不安全的(因为编译器无法判断整型值是否会超出char
能够表示的范围),因此编译器通常会产生警告。而如果我们使用的是列表初始化(括号初始化),编译器则通常会产生一个编译错误。(参见:括号初始化不允许隐式缩窄转换)
为了避免这些问题,我们可以使用 static_cast
显式地将整型转换为 char
:
1 2 3 4 |
|
这样做时,我们显式地告诉编译器这个转换是有意的,后果自负(例如,溢出 char
的范围)。由于这个 static_cast
的输出类型为 char
,变量 ch
的初始化的类型是匹配的,因此不会产生警告或错误。
下面是另一个编译器通常会抱怨将 double
转换为 int
可能会导致数据丢失的例子:
1 2 |
|
告诉编译器上述转换是有意而为之的:
1 2 |
|