C++四种类型转换符

一.const_cast

  • 用于修改类型的const或volatile属性。
  1. 常量指针转化为非常量的指针,并仍会指向原来的对象。
  2. 常量引用转化为非常量的引用,并仍会指向原来的对象。
  3. const_cast一般用于修改底指针,如const char* p形式。

例如:

const int num = 521;
int* p = const_cast<int*> (&num);//去掉const常量const属性

const int num = 521;
int& p = const_cast<int&> (num);//去掉const引用const属性

const char* num = “hello”;
char* p = const_cast<char*> (num);//去掉const指针const属性

二.static_cast

  • 从名字就可以看出来这种强制类型转换带有static属性,即编译阶段就生成好了,没有运行时类型检查保证转换的安全性,主要用法为:
  1. 用于类层次结构中基类和派生类之间指针或引用转换。
    进行上行转换(派生类->基类)是安全的。
    进行下行转换(基类->派生类)时,由于没有动态类型检查,所以不安全。
  2. 用于基本数据类型之间的转换,例如int->char; int->enum;这种转换也需要开发人员来确保安全性
  3. 把空指针转换成目标类型的指针。
  4. 把任何类型的表达式转换成void类型。

tips: static_cast不能转换const, volatile, __unaligned属性

例如:

char a = ‘a’;
int b = static_cast<char> (a);//正确

double *c = new double;
void *d = static_cast<void*> (c);//正确,将double指针转换成void指针

int e = 11;
const int f = static_cast<const int> (e);//正确,给int增加const属性

const int g = 22;
int *h = static_cast<int*> (&g);//error, static_cast不能转换掉const属性

类上行和下行转换:

if(Derived* dp = static_cast<Derived*> (bp)){//下行转换不安全!!
    //使用dp指向的Derived对象
}
else{
    //使用bp指向的Base对象
}

if(Base* bp = static_cast<Derived*> (dp)){//上行转换安全
    //使用bp指向的Derived对象
}
else{
    //使用dp指向的Base对象
}

三.dynamic_cast

dynamic_cast<type*> (e)
dynamic_cast<type&> (e)
dynamic_cast<type&&> (e)
  • type必须为一个类类型,第二种形式,type必须是左值;第三种形式,type必须是右值。在所有形式中,e的类型须符合以下三个条件中的任何一个:
    a. e的类型是目标类型type的公有(public)派生类。
    b. e的类型是目标type的共有基类。
    c. e的类型就是目标type的类型。
  • 若一条dynamic_cast语句的转换目标是指针类型并失败,返回0(NULL)。如果转换目标为引用类型并且转换失败,dynamic_cast运算符将抛出一个std::bad_cast异常(该异常定义在typeinfo标准库头文件中)。e也可以为一个空指针,结果是所需类型的空指针。
  • dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换(cross cast)。在类上行转换时,与static_cast效果一样;
  • 在下行转换时,dynamic_cast具有类型检查功能,相较于static_cast更加安全
  • dynamic_cast是唯一无法由C旧式语法执行的动作,也是唯一可能耗费重大运行成本的转型动作
    (1)指针类型
    例如,Base为包含至少一个虚函数的基类,Derived是Base的共有派生类,如果有一个指向Base的指针bp,我们可以在运行时将它转换成指向Derived的指针:
if(Derived* dp = dynamic_cast<Derived*> (bp)){
    //使用dp指向的Derived对象
}
else{
    //使用bp指向的Base对象
}

在上述代码,if语句中定义了dp,这样做的好处是可以在一个操作中同时完成类型转换条件检查两项任务。
(2)引用类型
因为不存在空引用,所以引用类型的dynamic_cast转换与指针类型不同,在转换失败时,抛出std::bad_cast异常。

void f(const Base& b){
    try{
        const Derived& d = dynamic_cast<const Base&> (b);
        //使用b引用的Derived对象
  }
  catch(std::bad_cast){
        //处理类型转换失败的情况
  }
}

四.reinterpret_cast

reinterpret_cast<new_type> (expression)

  • new_type必须是一个指针,引用,算术类型,函数指针或者成员指针。reinterpret_cast可以把一个指针转换成一个整数,也可把一个整数转换成一个指针(先把一个指针转换成整数,再把该整数转换成原类型的指针,还可以得到原先的指针值)。
    它意图执行低级转型,实际动作取决于编辑器,这表示它不可移植
    错误使用reinterpret_cast的例子:将整数类型转换成函数指针
#include <iostream>
using namespace std;
int output(int p){
    cout << p << endl;
    return 0;
}

typedef int (*test_func)(int );//定义函数指针test_func
int main(){
    int p = 10;
    test_func fun1 = output;
    fun1(p);//正确
    test_func fun2 = reinterpret_cast<test_func> (&p);
    fun2(p);//…处有未经处理的异常:0xC0000005: Access violation
    return 0;
}
  • 错误的使用reinterpret_cast很容易导致程序的不安全,只有将转换后的类型值转换回到其原始类型,这样才是正确使用reinterpret_cast方式
    在实际中可将reinterpret_cast应用到哈希函数,如下(64位系统需要将unsigned int修改为unsigned long):
#include <iostream>

//Return a hash code based on an address
unsigned short Hash(void* p){
    unsigned int val = reinterpret_cast<unsigned int> (p);
    return (unsigned short)(val ^ (val >> 16));//c强制转换,主要用于基础数据类型
}

using namespace std;
int main(){
    int a[20];
    for(int i = 0; i < 20; i++)
        cout << Hash(a + i) << endl;
}
  • reinterpret_cast与static_cast的区别在于多重继承,例如:
class A{
public:
    int m_a;
};

class B{
public:
    int m_b;
};

class C : public A, public B{};
C c;
printf(“%p, %p, %p”, &c, reinterpret_cast<B*> (&c), static_cast<B*> (&c));

前两个输出值相同,最后一个则在原基础偏移4个字节,这是因为static_cast计算了父子类指针转换的偏移量,并将之转换到正确的地址(c里面有m_a,m_b,转换为B*指针后指到m_b处),而reinterpret_cast却不会做这一层转换。因此,一定要谨慎使用reinterpret_cast。

五.C++强制转换注意事项

  • 新式转换较于旧式转换更受欢迎。一是因为新式转换较易辨别,能简化“找出类型系统在哪个地方被破坏”的过程;二是因为各转型动作的目标愈窄化,编译器愈能诊断出错误的作用。
  • 尽量少使用转型操作,尤其dynamic_cast耗时较高,导致性能下降。
posted @ 2022-07-20 14:33  yytarget  阅读(88)  评论(0编辑  收藏  举报