C/C++的显式类型转换

C/C++的显式类型转换

        在C/C++中,我们可以显式地将某个值、变量或对象强制转换成某种特定的类型。

C

         C语言中的显示类型转换非常简单,直接用括号把所需要转换的数据类型括起来放在值的左边即可:

int a = 100;
long b = (long)a;

        但是,还有一种是把值(变量)用括号括起来放在类型前面的做法:

int a = 100;
long b = long(a);

C++

        在C++中,除了包含以上提到的C语言转换方式,还添加了另外四个显式类型转换的语法,他们分别是:static_cast、dynamic_cast、const_cast和reinterpret_cast。

static_cast<type_id>(expression)

        static_cast支持典型的窄化转换(信息丢失)、void*强制转换、隐式类型转换和类层次的转换(不安全)。

窄化转换:

double d = 0.0;
int i ;
i = d; //由于double比int的占用字节数要大,所以从double转换到int会有信息丢失,一般这里会编译警告
i = static_cast<int>(d); //使用static_cast编译器不会报警告

void*前置转换:

int i = 5;
void* v = &i;
//int* t= v;  这一句会导致编译错误,C++不允许直接将void*转化为其他类型指针,需要强制转换 
int* j = static_cast<int*>(v);

隐式转换:

int a = 5;
double d  = a; //double总能容纳一个int,所以会隐式转换
long l = a;    //同理,long也是比int大
d = static_cast<double>a; //使用static_cast时为了突出这种转换,跟使用隐式转换没多大区别
l = static_cast<long>a;   //同上

         类层次的转换,这个跟dynamic_cast的作用一样,可以分别进行上行或下行的类层次的转换,这里把它放到与dynamic_cast一起使用进行对比。

dynamic_cast<type_id>(expression)

           dynamic_cast主要是一种类型安全的下行类型转换操作,它的type_id必须是类的指针、引用或者是void*,它与static_cast在类层次的转换有所区别。顾名思义的话,static_cast是静态的类型转换操作,而dynamic就是动态的类型转换,即根据实际对象动态地转换,这里所说的动态主要体现的是所传入的对象类型必须含有虚函数——因为它是靠存储在vtable上的类型信息来判断实际的类型,而dynamic_cast这种动态识别对象信息的能力又称RTTI(run-time type information),这是面向对象语言的一种机制,这里不再赘述。那么,dynamic_cast有了这种能力之后,与static在类层次上的转换有啥区别呢?

           首先,类型层次的转换又分上行与下行,其中上行转换总是安全的(因为一个父类的指针或引用,指向的是一个子类的对象总是安全的),那么下行转换呢?static_cast与dynamic_cast的不同就体现在这里了。看看下面代码:

class B
{
    public:
      int m_iNum;
      virtual void foo();
};
 
class D:public B
{
    public:
      char* m_szName[100];
};
 
void func(B* pb)
{
    D* pd1 = static_cast<D*>(pb);
    D* pd2 = dynamic_cast<D*>(pb);
}

        如果传给func函数的参数pb实际指向一个D类型的对象,那么无论pd1还是pd2都是正确的转换——指向一个D类型对象;若参数pb指向的是一个B类型的对象,则pd1是一个返回一个指向B对象的D*指针,用这个指针操作将是不安全的(例如访问m_szName[100],而程序员本身可能不知道就会出错),而pd2则是返回一个空指针。所以,一般来说,进行下行的类层次转换都是用dynamic_cast比较安全。不过也有例外,假如类层次中没有定义虚函数(这种设计本身就有问题,因为使用类很大程度就是为了利用其多态的性质),那么就无法使用dynamic_cast了,只能使用static_cast,前提是程序员要知道自己转换的类型是安全的,可以先通过type_id来判别类型(RTTI)再进行静态转换。

const_cast<type_id>(expression)

        很容易想象到的是,const_cast是用来进行const相关的转换,可以把一个const对象转换为一个非const对象,同理,反其道而行也是可以的;另外 还可以用来进行volatile的转换。一般来说,非const对象转换为const对象是没问题的,所以其实是不需要使用const_cast。主要是const对象转非const对象,这样就可以修改一个const对象的值了。另外,type_id必须是指针或引用。

const int a = 5;
int* b = const_cast<int*>(&a);
*b = 2;

reinterpret_cast<type_id>(expression)

        这是四种显式转换里最不安全、最容易出问题的转换。它是一种低级的位操作,可以把一个对象转换成另一种截然不同的类型,差不多即是重新定义了这个对象。它的type_id同样必须为指针或引用。

const int sz = 100;
class X
{
    public:
        int a [sz]; 
};

int main()
{
    X x;
    int* x = reinterpret_cast<int*>(&x);
    for(int*i = x; i < x + sz ; i++)
       *i = 0; //给x对象的内存清0
    //x->a[0] = 1;   a指针无法访问x对象的成员,这样做会使编译器报错
    return 0;
}
        reinterpret_cast的思想在于当需要的时候,所得到的东西(上面的例子是x指针)已经不同了,以至于它无法进行原来对象(x对象)的操作。

总结:使用C++的显式类型转换语法比C原有的强制类型转换更好更安全,同时当使用C++的显示类型转换在出错的时候更加容易发现并定位错误。



  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值