C++中四种类型转换分别为const_cast、static_cast、dynamic_cast、reinterpret_cast,以下表格是对四种转换类型一个简单的汇总。
类型 | 说明 |
---|---|
const_cast<type_name> (expression) | type_name必须是引用或指针,作用是const变量转为非const |
static_cast <type_name> (expression) | 将expression类型强制转换为type_name类型,使用较多 |
dynamic_cast <type_name> (expression) | type_name必须是类的指针,类的引用或者空类型的指针, 只能用于含有虚函数的类转换,用于类向上和向下转换 |
reinterpret_cast<type_name>(expression) | 做任何类型的转换,相同的比特位,极其不安全 |
为什么不用C的强制转换:C的强制转换表面上看起来功能强大什么都能转,但是转换不够明确,不能进行错误检查,容易出错。
1. const_cast
先来看一个例子,来简单了解一下const_cast的用法和注意事项:
#include<iostream>
using namespace std;
int main()
{
const int constant = 26; // 定义一个常量constant
const int* const_p = &constant; // 只能常量指针,才能指向常量
int* modifier = const_cast<int*>(const_p);
*modifier = 3; // const_cast使得指针指向的值可以修改
cout<< "constant: "<<constant<<endl; // 26
cout<<"*modifier: "<<*modifier<<endl; // 3
system("pause");
}
相关的说明我已经写在了注释里,这里需要强调一点,const_cast<type_name>中type-i必须是指针或是引用类型。
让我感觉奇怪的是,modifier和const_p应该指向的是constant内存区域,我分别打印了constant的地址、const_p指向的地址和modifier指向的地址,他们的地址是相同的,但constant和*modifier输出的值是不一样的(好多博客和我有一样的疑问,但我觉得没有解释清楚,这个疑问待解决)。
2. static_cast
在这里先说明什么是向上转换和向下转换:
向上转换:指子类向基类转换。
向下转换:指基类向子类转换。
这两种转换,子类包含父类,当父类转换成子类时可能出现非法内存访问的问题。
-
子类转换成父类
dynamic_cast <type_name> (expression)
比较安全,不会造成数据的丢失。
-
父类转换成子类
强制转换,不安全,可能会导致数据的丢失
该类型的转换称为最常用的的类型,从名字中可以看出,这种类型是静态的,除了基本类型转换外,派生类转换成基类(向上转换)是安全的;进行基类转换成派生类(向下转换)时,由于没有动态类型检查,所以是不安全的。那什么是动态类型检查,我理解的动态类型检查就是在程序运行过程中的检查,就像c++多态时用到的虚函数,那么static_cast更多适用在编译时就确定的类型。
以下介绍static_cast的基本使用:
#include<iostream>
using namespace std;
class Basic {
};
class Derive:public Basic{
};
int main() {
// 1. 基本类型转换
int a = 1;
double b = static_cast<double>(a);
cout << b << endl; //1
// 2. 空指针转换为目标指针
int *p;
void *m = malloc(sizeof(int));
p = static_cast<int *>(m);
*p = 233;
cout << *p << endl; // 233
// 3. 派生类转换成基类(向上转换、向下转换都可,只是向下转换容易出错)
Basic *bb = nullptr;
Derive *dd = new Derive;
bb = static_cast<Basic *> (dd);
cout << typeid(bb).name() << endl;
return 0;
}
3. dynamic_cast
使用此关键字有以下几个条件:
-
必须有虚函数(我试了gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.1), 不加虚函数也是可以的)
-
必须打开编译器的RTTI开关
-
必须有继承关系
dynamic_cast通过判断变量运行时类型和要转换的类型是否相同来判断是否能够进行向下转换。dynamic_cast可以做类之间上下转换,转换的时候会进行类型检查,类型相等成功转换,类型不等转换失败。运用RTTI技术,RTTI是”Runtime Type Information”的缩写,意思是运行时类型信息,它提供了运行时确定对象类型的方法。在c++层面主要体现在dynamic_cast和typeid,vs中虚函数表的-1位置存放了指向type_info的指针,对于存在虚函数的类型,dynamic_cast和typeid都会去查询type_info。
#include<iostream>
using namespace std;
class A {
public:
virtual void info() {
cout << "In A." << endl;
}
};
class B : public A{
public:
void info() {
cout << "In B." << endl;
}
};
int main() {
A a1;
B b1;
A* a2 = new A;
A* b2 = new B;
// B* d = dynamic_cast<B*>(a2);
// d->info(); // 出错,a2指向的是基类,基类转换成了派生类
B* d1 = dynamic_cast<B*> (b2);
d1->info(); // 可以, In B
// B &d2 = dynamic_cast<B&> (a2);
// d2->info(); // 出错
A* d3 = dynamic_cast<A*>(&b1);
d3->info(); // 可以,In B
return 0;
}
含义:将一个指向基类的指针转换成指向派生类的指针;如果失败,返回空指针。
dynamic_cast <type_name> (expression)
,该运算符将expression转换成type_name类型的对象。type_name必须是类的指针,类的引用或者空类型的指针。
转换参数的注意事项
- type_name和expression同类型。如果type_id是一个指针类型,那么expression也必须是一个指针类型,如果type_id是一个引用类型,那么expression也必须是一个引用类型;
- 如果type_id是一个空类型的指针,在运行的时候,就会检测expression的实际类型,结果是一个由expression决定的指针类型;
- 如果type_id不是空类型的指针,在运行的时候指向expression对象的指针能否可以转换成type_id类型的指针。
- 主要用于上下行之间的转换,也可以用于类之间的交叉转换。上行转换时和static_cast效果一样,下行转换时,具有检测功能,比static_cast更安全。
返回值
在运行的时候决定真正的类型,如果向下转换是安全的,就返回一个转换后的指针,若不安全,则返回一个空指针。
4. reinterpret_cast
reinterpret_cast可以做任何类型的转换,不过不对转换结果保证,容易出问题。