C++: 类型转换

C

  1. 隐式类型转换
    double d = 10.1;
    int i = d;    // double 转为 int
    char *str = "hello world";
    int *p = str;    // char * 转为 int *
    
    
    隐式类型转换在编译阶段完成,经常会有各种警告(编译器不一定能够猜测出我们的真实意图)。
    隐式类型转换存在风险。比如,double被转为int,存在数据精度的丢失(10.1变成了10);指针类型的转换前后同一个指针所能访问的范围是不一样的,比如char *一次能够访 问的内存块是单个字节,而int *一次能够访问的内存块是4字节,这就导致转换前后对同一个指针的使用可能出现非预期结果。
    另外,对于隐式类型转,编译时换经常会有各种警告,改成显示类型转换我们可以去掉它们。
  2. 显示类型转换
    double d = 10.1;
    
    int i = (int)d;
    
    char *str = "hello world";
    
    int *p = (int *)str;
    
    
    显示类型转换直接在要转换的成员前指明需要转换成的类型,简单粗暴;此时编译器不会给出警告,由此产生的潜在安全问题需要程序员自行考量。使用显式类型转换时,你要知道你在做什么。C++可使用这种显示类型转换。

C++

  1. reinterpret_cast
    格式:reinterpret_cast<type>(expression)

    double d = 10.1;
    int i = d;  // double转为int
    char *str = "hello world";
    int *p = reinterpret_cast<int *>(str); // char * 转为 int * 
    printf("i = %d, str = 0x%x, p = 0x%x\n", i, reinterpret_cast<unsigned int>(str), reinterpret_cast<unsigned int>(p));
    
    • type必须是一个指针、引用、算术类型、函数指针或者成员指针
    • 它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针
    • 跟C风格的显示类型转换类似,没有安全性检查

    注:无法更改const等属性

  2. const_cast

    上面的例子第三行会有警告

    warning: deprecated conversion from string constant to ‘char*’

    原因是字符串(“hello world”)默认是常量类型–只读,而char *默认是可读写的,所以会有冲突警告。消除警告的方法是前面添加 const 关键字声明char *为只读。

    另外,C++显示类型转换reinterpret_cast只能更改数据类型而无法更改读写属性,若要将const char *转为int *需要使用类型转换 const_cast:

    格式:const_cast<type>(expression)

    该运算符用来去除原来类型的const或volatile属性。
    除了const 或volatile修饰之外, type和expression的类型是一样的。

    double d = 10.1;
    int i = d;  // double转为int
    const char *str = "hello world";
    char *str2 = const_cast<char *>(str); // 去掉 const 属性
    int *p = reinterpret_cast<int *>(str2); // char *转为int * 
    printf("i = %d, str = 0x%x, p = 0x%x\n", i, reinterpret_cast<unsigned int>(str), reinterpret_cast<unsigned int>(p));
    

    从C++类型转换的格式可看出,C++的类型转换其实是方法(函数),<>表示该函数为模板函数。

  3. dynamic_cast
    常常有多个子类继承自同一个父类,当使用父类引用/指针等访问子类对象时我们需要知道其属于具体哪个子类,此时要用到动态类型转换 dynamic_cast,格式:
    dynamic_cast<type>(expression)

    #include <iostream>
    #include <string.h>
    #include <unistd.h>
    using namespace std;
    
    class Human {
    public:
        virtual void eating(void) { cout<<"use hand to eat"<<endl; }
        virtual ~Human() { cout<<"~Human()"<<endl; }
    };
    
    class English : public Human {
    public:
        void eating(void) { cout<<"use knife to eat"<<endl; }
        virtual ~English() { cout<<"~English()"<<endl; }
    };
    
    class Chinese : public Human {
    public:
        void eating(void) { cout<<"use chopsticks to eat"<<endl; }
        virtual ~Chinese() { cout<<"~Chinese()"<<endl; }
    };
    
    void test_eating(Human& h)
    {
        English *pe;
        Chinese *pc;
    
        h.eating();
    
        /* Is this person English or Chinese? */
        if (pe = dynamic_cast<English *>(&h))
            cout<<"This human is English"<<endl;
    
        if (pc = dynamic_cast<Chinese *>(&h))
            cout<<"This human is Chinese"<<endl;
    }
    
    int main(int argc, char **argv)
    {
        Human h;
        English e;
        Chinese c;
    
        test_eating(h);
        test_eating(e);
        test_eating(c);
    
        return 0;
    }
    

    动态类型转换只能用在含虚函数的类中。动态类型转换的机制:含虚函数的类的实例化对象中有一个指针,该指针指向一块包含虚函数表和类信息(含继承关系)的内容,从中可获知对象属于哪个类。类信息包含本类信息,还包含类的继承关系。比如,多重继承的时候,动态类型转换也能分辨出来:

    class JXman: public Chinese {
    public:
        void eating(void) { cout<<"use chopsticks to eat, I come from JX"<<endl; }
    };
    
    void test_eating(Human& h)
    {
        English *pe;
        Chinese    *pc;
        JXman *pj;
        
        h.eating();
    
        /* Is this person English or Chinese or JXman? */
        if (pe = dynamic_cast<English *>(&h))
            cout<<"This human is English"<<endl;
    
        if (pc = dynamic_cast<Chinese *>(&h))
            cout<<"This human is Chinese"<<endl;
        
        if (pg = dynamic_cast<JXman *>(&h))
            cout<<"This human is JXman "<<endl;    
    }
    
    int main(int argc, char **argv)
    {
        JXman j;
    
        test_eating(j);
    
        return 0;
    }
    

    上面会输出This human is Chinese和This human is JXman两条信息。

    值得注意的是,动态类型转换中推荐使用指针,而不是引用

    void test_eating(Human& h)
    {
        English& pe = dynamic_cast<English&>(h);  //如果一个引用不存在实体,就没有存在的必要了。这里会导致程序执行崩溃(编译没问题)。
        Chinese&    pc = dynamic_cast<Chinese&>(h);
       JXman& pj = dynamic_cast<JXman&>(h);
    
        h.eating();
    }
    
    int main(int argc, char **argv)
    {
        JXman j;
    
        test_eating(j);
    
        return 0;
    }
    

    动态类型转换有可能成功也有可能失败,失败时程序会异常终止,而强制类型转换reinterpret_cast可能会引入风险,所以dynamic_cast比reinterpret_cast更安全。例如:

    #if 0
        English& pe = dynamic_cast<English&>(h);  //如果一个引用不存在实体,就没有存在的必要了。这里会导致程序执行崩溃(编译没问题)。
        Chinese&    pc = dynamic_cast<Chinese&>(h);
       JXman& pj = dynamic_cast<JXman&>(h);
    #else
        English& pe = reinterpret_cast<English&>(h); //同样编译没问题,也能正常执行,但是有隐藏风险!
        Chinese&    pc = reinterpret_cast<Chinese&>(h);
       JXman& pj = reinterpret_cast<JXman&>(h);
    #endif
    

    总结一下:

    • dynamic_cast把expression转换成type类型的对象;type必须是类的指针、类的引用或者void *。如果type是类指针类型,那么expression也必须是一个指针;如果type是一个引用,那么expression也必须是一个引用。

    • 用于多态场合,即:必须有虚函数

    • 主要用于类层次间的上行转换(子类对象转换为父类对象)和下行转换(父类对象转换为子类对象),还可以用于类之间的交叉转换

    • 在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;

    • 在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。

  4. static_cast
    格式:static_cast<type>(expression)。静态类型转换在程序编译阶段进行,没有运行时类型检查来保证转换的安全性

    int main(int argc, char **argv)
    {
        Human h;
        //English e;
        //Chinese c;
        JXman j;
    
        English *pe;
        
        pe = static_cast<English *>(&h);//编译正常。但下行转换有风险(Human不一定是English),但static_cast检测不出来。
        English *pe2 = static_cast<English *>(&j);//编译报错。
        Chinese *pc = static_cast<Chinese *>(&j);//编译正常,执行正常。有继承关系的向上转换。
    
        return 0;
    }
    

    有如下特征:

    • 用于类层次结构中基类和子类之间指针或引用的转换。

    • 进行上行转换(把子类的指针或引用转换成基类表示)是安全的;

    • 进行下行转换(把基类指针或引用转换成子类指针或引用)时,由于没有动态类型检查,所以是不安全的,需要程序要自行确保程序逻辑的正确性。

    • 用于基本数据类型之间的转换,如把int转换成char,把int转换成enum:这种转换的安全性也要开发人员来保证。

    • 把void指针转换成目标类型的指针(不安全!!)

    • 把任何类型的表达式转换成void类型。

    注意:static_cast不能转换掉expression的const、volitale、或者__unaligned属性。


参考:韦东山C++教程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值