C++中四种强制类型转换区别详解

 C++即支持C风格的类型转换,又有本身风格的类型转换。C风格的转换格式很简单,可是有很多缺点的:程序员

  1.转换太过随意,能够在任意类型之间转换。你能够把一个指向const对象的指针转换成指向非const对象的指针,把一个指向基类对象的指针转换成一个派生类对象的指针,这些转换之间的差距是很是巨大的,可是传统的C语言风格的类型转换没有区分这些。express

  2.C风格的转换没有统一的关键字和标示符。对于大型系统,作代码排查时容易遗漏和忽略。ubuntu

  C++风格完美的解决了上面两个问题。1.对类型转换作了细分,提供了四种不一样类型转换,以支持不一样需求的转换;2.类型转换有了统一的标示符,利于代码排查和检视。下面分别来介绍这四种转换:static_cast、dynamic_cast、const_cast和reinterpre_cast.数组

1、static_cast转换安全

  1.基本用法:static_cast<type-id> expression函数

  2.使用场景:设计

  a、用于类层次结构中基类和派生类之间指针或引用的转换指针

  上行转换(派生类---->基类)是安全的;对象

  下行转换(基类---->派生类)因为没有动态类型检查,因此是不安全的。内存

  b、用于基本数据类型之间的转换,如把int转换为char,这种带来安全性问题由程序员来保证

  c、把空指针转换成目标类型的空指针

  d、把任何类型的表达式转为void类型

  3.使用特色

  a、主要执行非多态的转换操做,用于代替C中一般的转换操做

  b、隐式转换都建议使用static_cast进行标明和替换

2、dynamic_cast转换

  1.基本用法:dynamic_cast<type-id> expression

  2.使用场景:只有在派生类之间转换时才使用dynamic_cast,type-id必须是类指针,类引用或者void*。

  3.使用特色:

  a、基类必需要有虚函数,由于dynamic_cast是运行时类型检查,须要运行时类型信息,而这个信息是存储在类的虚函数表中,只有一个类定义了虚函数,才会有虚函数表(若是一个类没有虚函数,那么通常意义上,这个类的设计者也不想它成为一个基类)。

  b、对于下行转换,dynamic_cast是安全的(当类型不一致时,转换过来的是空指针),而static_cast是不安全的(当类型不一致时,转换过来的是错误意义的指针,可能形成踩内存,非法访问等各类问题)

  c、dynamic_cast还能够进行交叉转换

3、const_cast转换

  1.基本用法:const_cast<type-id>expression

  2.使用场景:

  a、常量指针转换为很是量指针,而且仍然指向原来的对象

  b、常量引用被转换为很是量引用,而且仍然指向原来的对象

  3.使用特色:

  a、cosnt_cast是四种类型转换符中惟一能够对常量进行操做的转换符

  b、去除常量性是一个危险的动做,尽可能避免使用。一个特定的场景是:类经过const提供重载时,通常都是很是量函数调用const_cast<const T>将参数转换为常量,而后调用常量函数,而后获得结果再调用const_cast <T>去除常量性。

4、reinterpret_cast转换

  1.基本用法:reinterpret_cast<type-id>expression

  2.使用场景:不到万不得已,不用使用这个转换符,高危操做

  3.使用特色:  

  a、reinterpret_cast是从底层对数据进行从新解释,依赖具体的平台,可移植性差

  b、reinterpret_cast能够将整型转换为指针,也能够把指针转换为数组

  c、reinterpret_cast能够在指针和引用里进行肆无忌惮的转换

5、各类转换之间的比较

  1.static_cast和dynamic_cast

复制代码

class Base
{
    public:
        Base(int c = 2):_c(c){}
    public:
        int _c;
};
class Derived:public Base
{
    public:
        int _d;
        int _e;
};
int main(void)
{
    int tempA = 2;
    int tempB = 3;
    Base base;
    
    /*1.无编译告警,可是危险操做,譬如说调用drvPtrA->_d会形成不可预知的后果*/
    Derived *drvPtrA = static_cast<Derived *>(&base);
    
    drvPtrA->_d = 4;
    drvPtrA->_e = 5;
    /*2.输出:tempA为5,tempB为4,踩内存了(机器信息:32位ubuntu,编译器clang++)*/
    cout<<tempA<<endl;
    cout<<tempB<<endl;
    
    /*3.Base中没有虚函数,没法查看运行时信息,编译不过*/
    Derived *drvPtrB = dynamic_cast<Derived *>(base);

    return 0;
}

复制代码

  在基类派生类互相转换时,虽然static_cast是在编译期完成,效率更高,可是不安全,上例中就示范了一个踩内存的例子。相比之下由于dynamic_cast能够查看运行时信息,上例若是Base含有虚函数,那么drvPtrB就是一个空指针(这可比踩内存什么的好多了),不能操做Derived中_d的数据从而保证安全性,因此应该优先使用dynamic_cast。

  2.static_cast和reinterpret_cast

复制代码

class BaseA
{
    public:
        BaseA(int c = 2):_c(c){}
        int _c;
};
class BaseB
{
    public:
        BaseB(int d = 3):_d(d){}
        int _d;
};
int main(void)
{
    BaseA baseA;
    /*1.编译不过*/
    BaseB *baseB = static_cast<BaseB *>(&baseA);
    /*2.无任何编译告警,编译经过,正常运行*/
    BaseB *baseC = reinterpret_cast<BaseB *>(&baseA);
    cout<<baseC->_d<<endl;

    return 0;
}

复制代码

  static_cast虽然也不是一种绝对安全的转换,可是它在转换时,仍是会进行必要的检测(诸如指针越界计算,类型检查)。reinterpret_cast彻底是肆无忌惮,直接从二进制开始从新映射解释,是极度不安全的,再次提醒,不到万不得已,不要使用。

相关文章
相关标签/搜索