C/C++是有类型语言,在表达式计算,表达式赋值和函数调用时都会发生各种类型转换。很多场合下,为了使上述类型转换合法、有效且满足特定需求,我们需要对表达式执行显式的类型转换。在这样的场合,如何选择合适的转换函数,是我们不得不面对的一个问题。本篇我们总结整理了cpp文档,对cpp中四种cast接口:const_cast
、static_cast
、dynamic_cast
、reinterpret_cast
进行了简要的总结。
const_cast
const_cast<new_type>(exp: type)
:在有不同const,volatile限定符的类型间转换。
-
功能:移除表达式的常量性和易变性。 例如调用某个形参为const T &的函数时,希望以(non-const)T类型作为参数。
-
分类:
- new_type和type为指向同一类型的指针,但是拥有不同的const,volatile限定符。
- 空指针值可转换成new_type的空指针值。
-
常用例子:
void Double(int & a) { cout << a << endl; a = a * 2; cout << a << endl; } int main(void) { const int tmp = 1; Double(const_cast<int &>(tmp)); }
static_cast
static_cast<new_type>(exp: type)
: 用隐式转换和用户定义转换的组合在类型间转换。
-
功能:显式、静态地执行隐式类型转换和用户定义的转换,不进行运行时检查。
-
分类:
- 隐式类型转换和大部分隐式转换的逆转换。
- 初始化转换: 存在初始化函数 new_type(type)。
- 子类到非虚父类的静态转换。
- 左值到右值,数组到指针,函数到指针。
- 弃值表达式:new_type为void。
- void * 到任何类型。
-
常用例子:
int n = static_cast<int>(3.14); //float 转 int std::vector<int> v = static_cast<std::vector<int>>(10); // 初始化转换 std::vector<int> v2 = static_cast<std::vector<int>&&>(v); //左值到右值 static_cast<void>(v2.size()); //弃值表达式 void* voidp = &n; std::vector<int>* p = static_cast<std::vector<int>*>(voidp); // void*转其他
dynamic_cast
dynamic_cast<new_type >(exp:type)
:沿继承层级向上、向下及侧向,安全地转换到其他类的指针和引用。
-
功能:在进行继承层级上的转换时,执行运行时检查,保证转型的安全性。
-
分类:
- 向下转型: type是new_type的公有基类,且从原对象中只能推导出一个new_type对象。
- 侧向转型: type和new_type相互无继承关系,但type和new_type都是原对象的公有基类。
- 向上转型: new_type是type的公有基类,可直接使用隐式类型转换或者static_cast。
- badcast: 均不满足,指向new_type的空指针。
-
常用例子:
// |--A--| // 虚基类 V-->| |-->D 继承关系表明:D同时继承了A、B // |--B--| A& a = d; // 向上转型,可以用 dynamic_cast,但不必须 D& new_d = dynamic_cast<D&>(a); // 向下转型 B& new_b = dynamic_cast<B&>(a); // 侧向转型
reinterpret_cast
reinterpret_cast<new_type>(exp: type)
:通过重新解释底层位模式从而在类型间转换。
-
功能:基本在原地实现任何类型的互转(去除cv限定符除外),但不保证使用安全性。
-
分类:
- 指针和整型互转:注意该整型需要保证size足够容纳指针数值。这同时包括:任何空指针(赋值为nullptr的指针)和空指针常量(nullptr),可转换成任何整型类型。
- 函数指针互转:函数指针之间的互转,不同类的成员函数指针的互转。
- 其他:type类型的左值表达式可转换成new_type &(注意无tmp变量生成,不发生拷贝);type * 可转 const/volatile new_type *; 任何具有空指针可转换为其他类型指针。
-
常用例子:
// 指针到整数并转回 int i = 7; std::uintptr_t v1 = reinterpret_cast<std::uintptr_t>(&i); int* p1 = reinterpret_cast<int*>(v1); // 到另一函数指针并转回:已知f为 int f() { return 0;} void(*fp1)() = reinterpret_cast<void(*)()>(f); // fp1(); 未定义行为 int(*fp2)() = reinterpret_cast<int(*)()>(fp1); // fp2(); 安全 // int 转 unsigned int & reinterpret_cast<unsigned int&>(i) = 42;
四种Cast的对比
通过上面的总结,我们大致可以明白四种cast接口的区别可由其名称前缀区分。
- 仅
const_cast
可以将const、volatile类型的cv限定符去掉。 - 仅
dynamic_cast
会做运行时动态类型检查,它主要用于多态类型的继承层级互转。 static_cast
使用范围较广,安全性较高。但当支持多态类的继承关系转换时,不会做运行时检查,存在一定的安全性问题。注意用static_cast
做转换时,生成的新对象是原对象的拷贝版本,只是执行了类型转换。reinterpret_cast
在编译器层面解构了原类型,以新类型的观点去解释原类型的各个字节,故基本可以做任何类型转换,例如不同类型函数指针互转,指针与整型互转等等。与static_cast
不同但与const_cast
类似的是,这种转换不会对原值的执行拷贝,故若能正确转回原类型,便能恢复变成原对象。很明显,reinterpret_cast
安全性问题较大。
参考资料
[1]. cppreference. https://en.cppreference.com/w/ 2020,2,22