C++ 类型转换

原文:http://www.wuzesheng.com/?p=1931

在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);
posted @ 2014-03-18 16:22  erictanghu  阅读(163)  评论(0编辑  收藏  举报