C++四种强制类型转换

什么是显示转换

有时我们希望显式地将对象转换为另外一种类型。例如,如果想在下面的代码中执行浮点数除法:

int i,j;
double slope = i/j;

就要使用某种方法将i/j的结果显示地转换为double。这种方法称作强制类型转换。

C风格的强制类型转换

C风格地进行强制类型转换包含两种形式:

type(expr);
(type)type;

C风格的强制类型转换从表现形式上来说不那么清楚明了,一旦出现问题,追踪起来比较困难。

C++风格的强制类型转换

C++风格的强制类型转换具有如下形式:

cast-name<type>(expression);

cast-name可以是static_castdynamic_castconst_castreinterpret_cast中的一种。

static_cast

static_cast会在编译期间对类型转换做安全检查,防止不必要的错误。几个主要的用法如下:

  1. 基础类型间的转换,如int转为char,float转为double。因为static_cast明确告诉编译器是在知情情况下发生的类型转换,所以对可能的精度损失不会告警。把精度大的值转换为精度小的值,static_cast会做截断处理。

    int64_t n1 = 0x12345678;
    int32_t n2 = n1;                                   //  warning: conversion from 'int64_t' {aka 'long long int'} to 'int32_t' {aka 'int'} may change value
    int32_t n3 = static_cast<int32_t>(n1);             //  no warning
    
  2. 把任意类型转换为void类型

     int64_t num=64;
     static_cast<void>(num);
    
  3. void*指针和目标类型指针的相互转换

    char strArr[11] = "helloworld";
    void *pVoid = static_cast<void *>(strArr);
    char* ptrStr=static_cast<char*>(pVoid);
    
  4. 用于基类和子类间的转换,但没有运行时类型检查保证转换安全,其中,子类转基类是安全的,但是父类转子类是不安全的

    class B {};
    
    class D : public B {};
    
    void f(B* pb, D* pd) {
      D* pd2 = static_cast<D*>(pb);   // Not safe, D can have fields
                                       // and methods that are not in B.
    
      B* pb2 = static_cast<B*>(pd);   // Safe conversion, D always
                                       // contains all of B.
    }
    
  5. static_cast不能进行无关类型(如非父类和子类)指针之间的转换

    int64_t n1 = 0x12345678;
    // error: invalid static_cast from type 'int64_t*' {aka 'long long int*'} to type 'char*'
    char* ptr = static_cast<char*>(&n1); 
    

const_cast

const_cast转换符是用来移除变量的const或volatile限定符。对于const变量,我们无法直接改变它的值,这个时候const_cast可以派上用场

void printAddr(int32_t* ptr)
{
    std::cout<<std::hex<<ptr<<std::oct<<std::endl;
}

int main()
{
    int32_t num=100;
    const int32_t* ptrNum=&num;
    //printAddr(ptrNum);     invalid conversion from 'const int32_t*' {aka 'const int*'} to 'int32_t*'
    printAddr(const_cast<int32_t*>(ptrNum));
}

reinterpret_cast

reinterpret字面意识是重新解释,reinterpret_cast可以理解为重新解释数值,它可以实现

  1. 任意类型指针转换为其他无关类型的指针

    struct Data
    {
        int32_t num;
        char ch;
    };
    
    int main()
    {
        Data data{0x12345678,'A'};
        int32_t* ptr=reinterpret_cast<int32_t*>(&data);
        std::cout<<std::hex<<*ptr<<std::endl;   //  0x12345678
    }
    
  2. 整形转换为任意类型指针

    uint64_t addr = 0x12345678FFFFFFFF;
    char* ptr=reinterpret_cast<char*>(addr);
    std::cout<<std::hex<<static_cast<void*>(ptr)<<std::endl;    // 0x12345678FFFFFFFF
    
  3. 任意类型指针转换为足够大的整形

    char ch = 'A';
    //uint32_t lAddr=reinterpret_cast<uint32_t>(&ch);   error: cast from 'char*' to 'uint32_t' {aka 'unsigned int'} loses precision
    uint64_t lAddr = reinterpret_cast<uint64_t>(&ch);
    std::cout << std::hex << "lAddr : " << lAddr << " , ch addr : " << static_cast<void *>(&ch) << std::endl;
    // llNum : 0x65fe17 , ch addr : 0x65fe17
    

reinterpret_cast不考虑转换数值类型之间是否是相关的,所以存在相当大的不安全性,如下例子整形指针转换为函数指针最终导致崩溃

typedef void(*pFunc)(int);

int main()
{
    int32_t num=100;
    pFunc pf=reinterpret_cast<pFunc>(&num);
    pf(3);
}

dynamic_cast

dynamic_cast用于将基类的指针或引用安全地转换成派生类的指针或引用(基类至少要包含一个虚函数),该转换特别适用于我们想使用基类对象的指针或引用执行某个派生类操作且该操作不是虚函数的情况。如果dynamic_cast转换失败了,如果转换目标是指针类型则返回空指针,如果是引用类型则会抛出一个bad_alloc异常

class CAnimal
{
public:
    virtual void eat()
    {
        
    }
    virtual ~CAnimal()
    {

    }
};

class CDog:public CAnimal
{
public:
    virtual void eat() override
    {
        
    }
    void bark()
    {
        std::cout<<"bark"<<std::endl;
    }
    virtual ~CDog()
    {
        
    }
};

int main()
{
    CAnimal* animal=new CDog;
    CDog* dog=dynamic_cast<CDog*>(animal);
    if (dog)
    {
        dog->bark();
    }
    delete animal;
}

PS:通过运行时类型识别(run-time type identification,RTTI),程序能够通过基类的指针或引用来检索其所指对象的实际类型。 dynamic_castC++RTTI的一种实现,另一个是typdid。对于带虚函数的类,在运行时执行RTTI操作符,返回动态类型信息;对于其他类型,在编译时执行RTTI,返回静态类型信息。

总结

  1. C++支持4种强制类型转换:static_castdynamic_castconst_castreinterpret_cast
  2. static_cast会在编译期间对类型转换做安全检查,多用于基础类型的转换,是工作中常用的一种显示转换
  3. dynamic_cast提供了RTTI的功能,支持将带虚函数的基类指针或引用安全地转换为派生类的指针或引用
  4. const_cast用于移除变量的const或volatile限定符
  5. reinterpret_cast支持任意指针类型间的相互转换,功能强大但也比较危险,使用时要谨慎

参考资料

[^1]https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2012/5f6c9f8h(v=vs.110)
[^2]https://www.ibm.com/docs/en/xl-c-and-cpp-aix/16.1?topic=operators-cast-expressions
  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值