首页 > 技术杂记 > C++类型转换(type cast)详解

C++类型转换(type cast)详解

在C中,类型转换相对比较简单,直接加括号强制转即可,不过这样做的后果,就是很难保证类型安全,所以,在C++中,虽然还允许C方式的类型转换,但是已经成为deprecated的方式了,良好的C++编程习惯都不推荐使用C方式的类型转换了。C++提供了类型相对安全的转换方式,本文的主要内容,就是介绍C++中的各种类型转换方式,以及各自用在什么样的场合下,有什么需要注意的地方,帮助大家更好的掌握类型转换,更好的驾驭自己的程序。
dynamic_cast,static_cast, const_cast和reinterpret_cast在C++中被统称为显式类型转换,在介绍它们之前,有必要介绍一下C++的隐式类型转换。隐式转换,就是不需要什么特殊运算符的转换,它是在变量赋值过程中自动发生的。在C++中,允许的隐式转换,主要包括以下两种方式:

  • 1. 基本类型之间的隐式转换
  • 1
    2
    
    short small_num = 1000;
    int big_num = small_num;

    上面的例子中,把一个16bit的整数,赋值给一个32bit的整数,在它们赋值的过程,就发生了隐式的类型转换。基本类型之间大都可以进行这样的转换,不过需要注意的是,把“小”的类型,赋值给“大”的类型,通常是OK的,反之,编译器可能会有warning, 也会有可能损失一些精度,比如把double(8bytes)赋给float(4bytes),即有warning, 又有精度损失。如果确实是想这样转换,尽量使用后面会介绍的显式的转换方式。

  • 2. 一个参数的构造函数
  • 1
    2
    3
    4
    5
    6
    7
    
    class Integer
    {
    public:
        Integer(int num);
    };
    int     a     = 10;
    Integer num_a = a;

    看上面的例子,Integer类有一个int类型参数的构造函数,所以可以直接把一个int的value赋值给Integer对象,这在C++中是安全的。不过这样的转换要用得小心,如果用得不当,会发生很诡异的结果。请看下面的例子:

    1
    2
    3
    4
    5
    6
    
    class Server
    {
    public:
        Server(const char * name);
    };
    Server svr = "hello world!";

    上面的程序,在语法上是完全OK的,编译也没有任何问题。不过在逻辑上,会让人觉得十会诡异,把一个字符串赋值一个Server对象,两个风马牛不相及的东西之间发生了转换。在这里,C++提供了一个explicit的关键字,告诉使用者,必须显式的调用构造函数来构造,不能用隐式转换的方式,请看改进后的程序:

    1
    2
    3
    4
    5
    6
    7
    
    class Server
    {
    public:
        explicit Server(const char * name);
    };
    Server svr1 = "hello world!"; ///< compile-error!!
    Server svr2("time sever");    ///< OK

    上面的程序中,直接赋值的方式编译器会告诉你错误的,这样就很好的避免了前面的问题。所以,关于一个参数的构造函数,使用着必须时刻保持清醒的头脑,不允许隐式转换的一定要加上explicit!
    通过上面的介绍,相信大家对隐式转换已经有了大致的了解,下面介绍一下C++中显式类型转换的几种方式:

  • 1. dynamic_cast
  • dynamic_cast只能用在指针和引用类型的转换中,它是唯一进行运行期(runtime)检查的类型转换符,它的主要目的就是保证转换后的类型是一个完整类型(Complete type)。dynamic_cast在转换指针类型时,如果结果不是一个Complete Type, 它会返回NULL; dynamic_cast在转换引用类型时,如果结果不是一个Complete Type,它会抛出bad_cast的异常。看下面的例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    
    class Car 
    { 
        /// ...
    };
     
    class BMW : public Car
    {
        /// ...
    };
     
    class Benz : public Car
    {
        /// ...
    };
     
    bool Repair(Car * car)
    {
        BMW * bmw = dynamic_cast< BMW *>(car);
        if (bmw != NULL) ///< a BMW car
        {
            return RepairBMW(bmw);
        }
     
        Benz * benz = dynamic_cast< Benz *>(car);
        if (benz != NULL) ///< a Benz car
        {
            return RepairBenz(benz);
        }
        return false; ///< not BMW and Benz
    }

    在上面的例子中,BMW和Benz是从Car继承过来的子类型,Repair函数接受一个Car指针类型参数,然后根据具体的类型,调用相应的Repair函数,BMW调用RepairBMW, Benz调用RepairBenz。在C++中,多态类型转为实际的类型,尽量使用dynamic_cast,这样可以利用dynamic_cast的运行期检查,更好地保证程序的正确性。

  • 2. static_cast
  • static_cast是最为常用的cast,它所做的工作,就是进行“逻辑上”正确的类型转换。它可以用在各种类型的转换中,包括指针类型、引用类型和普通类型。static_cast没有运行期的类型检查,因此也没有因运行期类型检查带来的额外开销,但是需要使用者自己保证转换后结果的完整性,否则可能会出现runtime error。下面是一些static_cast的例子:

    1
    2
    3
    4
    5
    
    double d = 3.14
    int    n = static_cast< int>(d);
     
    Car * car = new BMW();
    Benz * benz = static_cast< Benz *>(car);

    上面两种转换,逻辑上都是正确的。但是,需要使用者保证,第一个中精度的损失是允许的,第二个中,BMW向Benz之间是否可以转换。

  • 3. const_cast
  • const_cast的用法相对比较简单,它主要用来给指定类型增加、或者移除const属性。另外,const_cast也可以用于各种类型,包括指针类型、引用类型和普通类型。看下面的例子,移除/增加char *类型的const属性。

    1
    2
    3
    
    const char * msg1 = "hello world";
    char * msg2 = const_cast< char *>(msg1);
    const char * msg3 = const_cast< const char *>(msg2);
  • 4. reinterpret_cast
  • reinterpret_cast的行为是implementation-defined,它的实现可能是按bits重新interpret,但是不能总是保证这一点,所以用interpret_cast的程序,移植性不是很好,尽量少用。reinterpret_cast也可以用来转换各种类型,包括指针类型、引用类型和普通类型。看下面的例子,把一个32bit的整数,reinterpret成一个32bit的对象。

    1
    2
    3
    4
    5
    6
    
    int a = 10;
    struct Num
    { 
        int m_a;
    };
    Num num = reinterpret_cast< Num>(a);
    1. 2011年8月8日21:22 | #1

      发现最后的那个没用过

    2. 2011年8月8日22:01 | #2

      前面三个用得多一些吧@leon

    3. 观雨
      2011年10月26日10:43 | #3

      const_cast主要是移除const的转换,增加const没有必要用const_cast吧?可以直接把非常量的变量值赋给常量变量,应该也属于一种隐私转换。

    4. 2011年10月26日12:27 | #4

      恩,是的,只不过用const_cast也可以达到同样的目的。@观雨

    1. 本文目前尚无任何 trackbacks 和 pingbacks.
    您必须在 登录 后才能发布评论.