C++强制类型转换总结

C/C++是有类型语言,在表达式计算,表达式赋值和函数调用时都会发生各种类型转换。很多场合下,为了使上述类型转换合法、有效且满足特定需求,我们需要对表达式执行显式的类型转换。在这样的场合,如何选择合适的转换函数,是我们不得不面对的一个问题。本篇我们总结整理了cpp文档,对cpp中四种cast接口:const_caststatic_castdynamic_castreinterpret_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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章