C++ 类型转换

 C中,类型转换相对比较简单,直接加括号强制转即可,不过这样做的后果,就是很难保证类型安全,所以,在C++中,虽然还允许C方式的类型转换,但是已经成为deprecated的方式了,良好的C++编程习惯都不推荐使用C方式的类型转换了。C++提供了类型相对安全的转换方式,本文的主要内容,就是介绍C++中的各种类型转换方式,以及各自用在什么样的场合下,有什么需要注意的地方,帮助大家更好的掌握类型转换,更好的驾驭自己的程序。
      dynamic_cast
static_cast, const_castreinterpret_castC++中被统称为显式类型转换,在介绍它们之前,有必要介绍一下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类型参数的构造函数,所以可以直接把一个intvalue赋值给Integer对象,这在C++中是安全的。不过这样的转换要用得小心,如果用得不当,会发生很诡异的结果。请看下面的例子:

1

2

3

4

5

6

class Server

{

public:

    Server(constchar* name);

};

Server svr ="hello world!";



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

1

2

3

4

5

6

7

class Server

{

public:

    explicit Server(constchar* 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);

    }

    returnfalse;///< not BMW and Benz

}


1 class BaseClass {
2 public:
3 int m_iNum;
4 virtualvoid foo(){}; //基类必须有虚函数。保持多台特性才能使用dynamic_cast
5 };
6
7 class DerivedClass: public BaseClass {
8 public:
9 char*m_szName[100];
10 void bar(){};
11 };
12
13 BaseClass* pb =new DerivedClass();
14 DerivedClass *pd1 = static_cast<DerivedClass *>(pb); //子类->父类,静态类型转换,正确但不推荐
15 DerivedClass *pd2 = dynamic_cast<DerivedClass *>(pb); //子类->父类,动态类型转换,正确
16
17 BaseClass* pb2 =new BaseClass();
18 DerivedClass *pd21 = static_cast<DerivedClass *>(pb2); //父类->子类,静态类型转换,危险!访问子类m_szName成员越界
19 DerivedClass *pd22 = dynamic_cast<DerivedClass *>(pb2); //父类->子类,动态类型转换,安全的。结果是NULL

      在上面的例子中,BMWBenz是从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);

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

·  3. const_cast

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

1

2

3

constchar* msg1 ="hello world";

char* msg2 =const_cast<char*>(msg1);

constchar* msg3 =const_cast<constchar*>(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 int doSomething(){return0;};
2 typedef void(*FuncPtr)(); //FuncPtr is 一个指向函数的指针,该函数没有参数,返回值类型为 void
3 FuncPtr funcPtrArray[10]; //10个FuncPtrs指针的数组 让我们假设你希望(因为某些莫名其妙的原因)把一个指向下面函数的指针存入funcPtrArray数组:
4
5 funcPtrArray[0] =&doSomething;// 编译错误!类型不匹配,reinterpret_cast可以让编译器以你的方法去看待它们:funcPtrArray
6 funcPtrArray[0] = reinterpret_cast<FuncPtr>(&doSomething); //不同函数指针类型之间进行转换

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值