C++类型转换
300 Words | Read in about 1 Min | View times
Overview
C++的类型转换分为隐式转换和显式转换。数值之间如何转换、指针之间如何转换、explicit关键字又是什么、强制类型转换操作符的区别又是什么,本节内容将一一解答。
本系列文章将包括以下领域:
本章其他内容请见 《现代C++》
隐式转换
隐式类型转换是编译器自动推导的,无需显式操作符。隐式类型转换往往发生在函数实参到形参的转换过程、函数返回值的转换过程等。
数值类型转换
从小的整形(char、short)向大的整形(int、long)转换,float向double转换,都属于数值类型转换,向上提升的类型转换只是内存存储形式的变化,不会对数值有影响。
但是有一些数值类型转换可能会引起编译器警告:
- 浮点数转成整形数,数值的小数部分会被直接截断丢弃,造成精度丢失。
1float foo = 1.5f;
2int bar = foo; //b = 1,丢失精度
- 有bool类型参与的转换,false等价于0或空指针,true等价于任何非0数值。
1bool foo = true;
2int bar = foo; //bar = 1
3
4int hello = -2;
5bool world = hello; //world = true
- 负数转成无符号数,由于负数使用补码表示,编译器会产生警告。
1int foo = -1;
2unsigned int bar = foo; //bar = 2^32 - 1 = 4,294,967,295
指针类型转换
-
子类指针转成父类指针(
const、volatile不会变)。 -
C风格数组首元素隐式转成一个指针,指向数组第一个元素。
1//bad code, but works
2
3char* p = "CPlusPlus" + 3; //"CPlusPlus"隐式转成一个指针p指向首元素'C',p向后移动3个元素,指向'u'
显式转换
C风格的强制类型转换在C++中应尽量避免,容易出错引发未定义的行为。
1//old style, should not use in C++
2int foo = (int)bar;
3int hello = int(world);
explicit关键字
explicit关键字一般用于构造函数,且构造函数只有一个参数,用来阻止通过一个参数类型的对象直接隐式调用了构造函数而转换成另一个类型。
1class A { };
2
3class B {
4public:
5 B(const A& a) {}
6};
7
8class C {
9public:
10 explicit C(const A& a) {}
11};
12
13int main() {
14 A foo;
15 B bar = foo; //隐式调用构造函数进行隐式转换
16
17 // C cool = foo; //报错,不允许转换
18 C cool(foo); //显式调用构造函数
19 return 0;
20}
四种强制类型转换
C++提供4种进行安全类型强制转换的操作符:static_cast、const_cast、dynamic_cast、reinterpret_cast
static_cast
1static_cast<T>(expr);
在编译期做强制类型转换,expr的const和volatile属性得到保留。一般有以下使用场景:
-
基本数据类型之间的转换,如
char转int,int转enum等 -
任意类型指针转成
void*指针,反之不安全 -
转换为右值引用
-
子类指针或引用转成父类指针或引用,反之不安全
const_cast
1const_cast<T>(expr);
T为一个指针或引用,或者指向对象类型成员的指针,用于去除对象的const或volatile属性。
1const std::string& str = "hello world";
2
3std::string newStr = const_cast<std::string&>(str);
4
5newStr[0] = 'H'; //newStr = "Hello world";
dynamic_cast
1dynamic_cast<T>(expr);
T为一个指针或引用,或者void*指针,是RTTI机制的一个操作符,用来确保指向一个完整的目标类型。
若T为指针,则expr也必须为指针,转换失败返回nullptr;若T为引用,则expr必须为左值,转换失败会抛出bad_cast异常。
后面小节我们会继续展开聊一聊RTTI那些事儿。
reinterpret_cast
1reinterpret_cast<T>(expr);
T为指针、引用、基本类型、函数指针或成员指针,一般在底层代码中才会使用。不会改变比特位数据,而是用新的类型来解释这些比特位,故名为reinterpret转换。