C++类型转换
类型转换,顾名思义即将操作数转化为所需要的类型。C++作为C语言的超集,完全继承了C语言所具有的类型转换方法与能力。C语言有两种类型转换方式:隐式转换(implicit)和显示转换(explicit)。示例如下:
int a = 10; double b = a; // 隐式转换 int c = (int)b; // 显示转换
容易看出,这两种转换方式存在某些缺陷,如意图不够明确,安全性得不到保证(尤其是对引进了class类型的C++语言)。因此,C++引进了4种类型转换操作符(static_cast, const_cast, dynamic_cast, reinterpret_cast)以及标识符explicit。接下来我将对以上4个转换操作符和1个标识符进行讲解。
一、static_cast
static_cast在C++中属于较常使用的类型转换符,与C语言的显示转换方式作用一样,并且同样没有提供运行时类型安全检查。使用这种转换方式时需注意它不能将操作数的const、volatie等属性移除。示例如下:
int m = 10; const int n = 100; double a = static_cast<double>(m); // 等价于 a = (double) m ; double b = static_cast<double>(n); // 正确 double *c = static_cast<double*>(n); // 错误, 必须在第一个double前加上const
二、const_cast
此转换可将操作数的const 或者 volatie 属性移除而其他属性不变(注意移除并不是指原常量就可以随便更改,只是新赋值的变量才可以)。const_cast一般用于赋值指针或者引用。示例如下:
const int a = 10; const int *pa = &a; // const不能少,不能通过*pa改变a的值 int *b = const_cast<int *>(&a); // 可以通过*b修改pa所指变量 *b = 20; cout << a << " " << *pa << " " << *b << endl;
cout的输出结果为 10 20 20
补充知识:常量折叠
编译器进行语法分析时,会将常量或者常量表达式计算求值后替换表达式,并放入常量表。之后编译器在优化的过程中会把碰见的const内容替换掉,假若遇到对常量的的取址或者外部声明操作,则编译器会在Data去为该常量分配一块内存以使得相关操作合法。
对于上例中的输出,a在编译阶段就被会被替换成10。
三、dynamic_cast
与static_cast刚好相反,dynamic_cast用于运行时检查转换是否安全,一般用于多态编程中父类、子类指针间的转换。当其作用于子类升父类的转换时,效果与static_cast一样,反过来作用于父类指针到子类指针的下行转换时其强大的功能便会凸显。示例如下:
四、reinterpret_cast
reinterpret中文译为重新解释,此变换在于将操作数的二进制形式数据重新解释,如int转换成char。此转换通常带来不可移植性,故使用场合较少。
五、explicit
一般情况下若某个类的构造函数的传入参数只有一个或者第二个参数开始都设置过默认参数,则实例化该类对象时可使用隐式变换方法,如下例main函数中的a对象和b对象。但有时这种隐式变换会导致某些预想不到的后果(非语法错误,只是有些内容复制不妥当等),这时我们就想阻止这种隐式变换,explicit关键字就派上用场了。
public:
A(int id): Id(id){}
private:
int Id;
};
class B{
public:
B(int id, string nn = "XiaoMing"): Id(id), name(na){}
private:
int Id;
string name;
};
class C{
public:
explicit C(int id): Id(id){} // 添加explicit关键字,表明C对象只能进行显示转换
private:
int Id;
};
int main(){
A a= 12; // 正确,12可以隐式转换成A类型, 编译器会隐式调用其构造函数
B b = 34; // 正确,34可以隐式转换成B类型, 编译器会隐式调用其构造函数
C c1 = 45; // 错误,不支持隐式转换
C c2 = C(45); // 正确,可进行显示转换
}