类型转换

今天讲的是C++的类型转换(比较无聊的内容,但最好看看,因为可以帮助大家减少程序中的bug)。许多学过C的朋友一定还记得C语言中的类型转换,例如:

float FloatNum = 1.234;
int IntNum = (int)FloatNum;
// IntNum = 1

这是比较正常的类型转换,稍微危险一点的转换如下:

float FloatNum = 1.234;
float * pFloatPointer = &FloatNum;
int * pIntPointer = (int *)pFloatPointer;
// *pIntPointer 里边就是乱七八糟的东西了

C 的类型转换虽然很方便,但是却带来了更多的问题。比如 C的类型转换可以允许你进行任意类型之间的转换。 非常随意的使用类型转换很容易造成程序逻辑的混 乱,使人看不懂你写的代码,或者编译器不能正确识别你的转换的意图,所以做出错误的转换方式。其次,C类型的转换很难查错。特别是在大型的工程中,你想找 出一个因为 (uint)转换成 int而产生益出的问题,可能需要查看上千行包含"(int)"的代码。为了避免诸如此类情况的发生 C++引入了4种 类型转换的方式。请记住,C的类型转换只是为C语言设计的,并不适合C++。C++支持C的类型转换只不过是为了向下兼容的考虑。

让我们来看看C++的4种类型转换(关于这4种类型转换的详细说明,请参见<C++ Programming Language> 3rd Edition):

static_cast<>()
dynamic_cast<>()
const_cast<>()
reinterpret_cast<>()

我们一个一个来说
================== static_cast ==================
static_cast<>() 
static_cast可以用来进行相关类型之见的转换,比如double 转成 float, float转成 int 以及 有关联的pointer之间,有关联的 class pointer 之间的转换。

========比如========

float FloatNum = 1.234;
int IntNum = static_cast<int>(FloatNum);    // IntNum = 1;

========或者========

class BaseClass{
public:
    BaseClass();
    virtual ~BaseClass();
};

class DerivedClass : public BaseClass{
public:
    DerivedClass();
    ~DerivedClass();
    void DoSomething(void);
};

BaseClass * pBaseClass = new BaseClass();
// 没问题,不过call pDerviedCalss->DoSomething()很有可能会crash
DerivedClass * pDerivedClass = static_cast<DerivedClass *>pBaseClass;

DerivedClass * pDerivedClass = new DerivedClass();
// 没问题,很安全
BaseClass * pBaseClass = static_cast<BaseClass *>(pDerivedClass);

值得注意的是 static_cast是在程序编译的时候检查类型转换是否符合要求,并不 在程序运行期间进行检查,因为没有 runtime overhead, 对速度的影响比较小。所以在你对类型转换很有把握的时候,可以尽量的使用static_cast。另外  static_cast在转换指针的时候并不能保证转换前的指针地址和转换后的指针地址相同,特别是在多重继承的类结构中,指针地址经常会变化。

下面是一些通常比较危险的类型转换,

========包括========
unsigned 转 sign 比如 uint 转成 int
double 转 float
long 转 int (64位操作系统有危险,32位无)

这些转换都是值域大的转成值域小的数,或者无符号转成有符号。比如:

unsigned int k = 4294967290;
int m = static_cast<int>(k);

这里k已经超出了int的值域,所以 m的最后结果是 -6。所以在做以上的类型转换的时候,要特别的小心。


================== dynamic_cast ==================
dynamic_cast 是用来对相关的class 指针之间进行的类型转换。由于dynamic_cast在运行过程中对转换进行安全性检查,所以在很大程度上影响程序的运行速 度,并且在便宜的时候需要打开runtime type info 的开关 /GR,所以并不推荐使用。

如果要使用dynamic_cast那么要求转换的类至少需要含一个虚函数,并且只能对类的指针进行转换操作,指针不包括 void *。 例如:

class BaseClass{
public:
    BaseClass();
    virtual ~BaseClass();
    virtual void DoSomething(void);
};

class DerivedClass : public BaseClass{
public:
    DerivedClass();
    ~DerivedClass();
    void DoSomething(void);
};

DerivedClass * pDerivedClass = new DerivedClass();
// 没问题
BaseClass * pBaseClass = dynamic_cast<DerivedClass *>(pDerivedClass);

BaseClass * pBaseClass = new BaseCalss();
// 有问题,基类转成派生类,dynamic_cast会返回null 所以 pDerivedClass == NULL
DerivedClass * pDerivedCalss = dynamic_cast<DerivedClass *>(pBaseClass);

================== const_cast ==================
顾名思义,就是把const变量转换成 non-const变量,或者把volatile转成non-volatile。这是最不推荐使用的一种转换,只有在特殊的情况下才会使用。如果你需要大量的使用const_cast,那么只能说明程序中存在着设计缺陷。

const_cast的使用如下:
float FloatNum = 12;
const float * pConstFloatPointer = &FloatNum;
// ok 这么用没问题
float * pFloatPointer = const_cast<float *>(pConstFloatPointer);
// 编译错误,const_cast只能进行const和non-const之间的转换
int * pFloatPointer = const_cast<int *>(pConstFloatPointer);

================== reinterpret_cast ==================
reinterpret_cast是最危险的一种转换类型,它并不对数据进行任何实际的转换操作,而是直接把数据当作另一种类型来使用(跟内存拷贝的功能差不多)。比如:

class ClassX{...};
class ClassY{...};

ClassX * pClassX = new ClassX();
// 没问题,不过除非classX和classY都是结构相同的interface,否则并不安全,程序很容易crash
ClassY * pClassY = reinterpret_cast<ClassY *>(pClassX);

reinterpet_cast比较有用的地方就是函数指针的转换,比如把一个指针存到一个函数指针数组中:

      typedef void (*FuncPtr)();
      FuncPtr FuncPtrArray[10];
      int DoSomething() {...};
      // 这里使用reinterpret_cast
      FuncPtrArray[0] = reinterpret_cast<FuncPtr>(&DoSomething);

总的来说,尽可能的使用这4种转换来清晰的表达你转换的目的,尽量不要使用C风格的转换

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值