C++类型转换(Type Casting)

所谓类型转换即为转换一个给定的类型到另一个类型,我们已经知道一些类型转换方式:

隐式类型转换

       隐式类型转换不需要请求任何操作。当一个值拷贝给另一个兼容的类型的时候会自动执行,例如:

short a = 200;
int b;
b = a;

       在此例中,a的值从short类型到int类型提升,并且不需要任何类型转换操作。这个转换是我们都知道的标准转换。标准转换影响基本的数据类型,并且只能转换从数值类型(short->int,int->float,double->int等)到或者从bool,和一些指针转换。一些转换可能会丢失精度,这样的话编译器会提示警告,在显示转换中不会出现警告。

       隐式类型转换也包括构造函数或者操作符转换,例如:

classA {};
class B {public: B (A a) {} };
A a;
B b=a;

在此实例中,在类A和类B对象之间发生隐式转换,因为B带一个参数为类A对象。因此隐式类型转换从A到B是允许的。

显示类型转换

       C++是一个强类型的语言,许多转换尤其是那些指定值的不同解释,需要显示转换。我们已经知道两个显示类型转换的标记:函数和c样式的类型转换:

shorta = 200;
intb;
b=  (int)a;   //C样式类型标记
b =  int (a);   //函数类型标记

对于大多数需求,对于函数类型函数的显示类型操作已经足够。但是,如果这些操作无差别的应用到类和指向类的指针上,可能导致在语法检查中是正确的,但是运行时出现错误。例如,下面的代在码语法结构上正确:

#include <iostream>
using namespacestd;
class CDummy{
     floati,j;
};
class CAddition{
     intx,y;
public:
     CAddition(inta, int b){ x = a;y = b;}
     intResult(){returnx+y;}
};
int _tmain(intargc,_TCHAR*argv[])
{
     CDummyd;
     CAddition*padd;
     padd= (CAddition *)&d;
     cout<<padd->Result()<<endl;
     return0;
}

这个程序声明一个CAddition指针,然后通过显示类型转换分配给此指针另一个不相容类型对象的引用:

padd = (CAddition*)&d;

     传统的显示类型转换允许转换任何指针到任何其他类型的指针,并且它们指向的类型是完全独立地。随后的调用result成员函数会产生或者运行时错误或者不期望的值。

    为了控制在类之间的转换类型,c++有四个指定的转换操作:dynamic_cast、static_cast、const_cast和reinterpret_cast。它们的格式遵从新的类型封装,这个封装在尖括号和随后紧跟的在圆括号中的需要转换的表达式之间转换。

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

传统的类型转换和这些表达式等效表达为:

(new_type)expression
 new_type(expression)

但是每一个都有自己特定的特征:

dynamic_cast

dynamic_cast仅用于对象的指针和引用,它确保类型转换记过是一个完全合法的请求类对象。

因此,dynamic_cast在转换一个类到一个其基类的时候,总是成功:

class CBase{ };
class CDerived:public CBase{ };
CBase b; CBase* pb;
CDerived d; CDerived* pd;
pb = dynamic_cast<CBase*>(&d);    // ok:derived-to-base
pd = dynamic_cast<CDerived*>(&b); // wrong:base-to-derived

在此程序片段中的第二个转换会产生一个编译错误,因为在dynamic_cast中基类到继承类转换是不允许的,除非基类是多态的。

当一个类是多态的,dynamic_cast在运行时执行一个指定的检查,确保表达式产生一个合法的请求类的完成对象:

    

#include <iostream>
#include <exception>
class CBase{virtual voiddummy(){}};
class CDerived:public CBase{int a;};
int main()
{
     try
     {
         CBase *pba =new CDerived;
         CBase *pbb =new CBase;
         CDerived *pd;
         pd = dynamic_cast<CDerived *>(pba);
         if (pd == 0)
         {
              cout<< "NULLpointer on first type-cast" << endl;
         }
         pd = dynamic_cast<CDerived *>(pbb);
         if (pd == 0)
         {
              cout<<"NULLpointer on second type-cast" <<endl;
         }
     }
     catch (exception*e)
     {
         cout<< "Exception:"<< e->what()<<endl;
     }
     return 0;
}

执行结果:NULL pointer on second type-cast

兼容提示:dynamic_cast要求实时类型信息(RTTI)保持对动态类型的跟踪。一些编译器支持这个特征作为可选项并且默认为不支持的。在用dynamic_cast进行实时类型检查时,这个需要修改成支持模式才能保证正常工作。

上面的代码执行两个动态类型转换从CBase*到CDerived*,但是仅第一个转换是成功的,这个需要注意它们各自的初始化不同:

CBase *pba=newCDerived;

         CBase *pbb =newCBase;

    虽然这两个都是CBase*指针,pba指向类型CDerived对象,而pbb指向类型CBase对象。然后,当它们各自用dynamic_cas t执行类型转换,pba指向完整的类CDerived对象,而pbb指向类CBase对象,并且pbb是一个不完整的类CDerived对象。

    当dynamic_cast不能转换一个指针,由于一个请求类的不完整对象时,正如在上例中的第二个转换,返回空指针指示转换失败。如果dynamic_cast用于转换一个引用类型并且转换是不可能时,一个类型为bad_cast异常抛出。

    dynamic_cast也可以转换空指针甚至在不相关的类指针,并且也可转换任何类型的指针到空指针(void *)。

static_cast

    static_cast可以在相关的类之间执行转换,不仅仅从派生类到基类,还可以从基类到派生类。这确保如果合适的对象被转换,至少类是相关的。在运行期间没有安全检查,假设转换的对象之间是一个完整对象类型(两个类型是同种类型)。因此,这需要编程人员确保转换时安全的,另一方面,前面的类似dynamic_cast的类型安全检查是没有的(avoided)。

class CBase{};
class CDerived:public CBase{};
CBase * a= new CBase;
CDerived * b=static_cast<CDerived*>(a);

    这个片段可能是合法的,虽然b可能指向一个不匹配的类对象,可能导致运行时错误如果解除参照。

    static_cast也可以用于执行任何其它的非指针转换,这些转换可以印刷转换执行,例如基本的类型标准转换:

    double d=3.14159265;
     int i =static_cast<int>(d);

    或者任何在有显示的构造函数或者操作符函数的类之间转换,此种在上面“隐式转换“中描述过。

reinterpret_cast

    reinterpret_cast转换任何指针类型到任何其他的指针类型,甚至是不相关的类。操作结果是一个简单的从一个指针到另一个指针的二进制值拷贝。允许所有的指针转换:即不检查指针内容也不检查自身的指针类型。

    也可以转换指针到(从)整数类型。代表一个指针的指针值的格式是平台指定的。仅有的保证是一个指针转换到一个整数类型需要足够包括转换的值,并且需要转换回到合法的指针。

    在c++中由reninterpret_cast执行而不是由static_cast执行的转换是低级别的操作,在系统指定的产生的代码中的执行结果是不方便的,例如:

    class A{};
class B{};
A * a= new A;
B * b= reinterpret_cast<B*>(a)

    这是合法的c++代码,虽然没有什么意义。你需要谨慎使用reinterpret_cast。

const_cast

    转换类型操作对象常量,或者设定或者移除(常量)。例如,为了传递一个常量参数到一个希望得到非常量参数的函数:

 // const_cast
#include <iostream>
using namespacestd;
void print(char * str)
{
     cout << str<< endl;
}
int main() {
     const char * c = "sampletext";
     print ( const_cast<char *> (c) );
     return 0;
}

运行结果:sampletext

typeid

typeid允许核对表达式的类型:

typeid(expression)

这个操作符返回一个type_info类型的常对象引用,type_info类型是一个定义在标准头文件<typeinfo>中。这个返回值可以和另一个值用==和!=比较或者获得一个代表数据类型的非终止的字符序列或者通过它的name()成员函数获得的类名。

// typeid
#include <iostream>
#include <typeinfo>
using namespacestd;
int main() {
     int * a,b;
     a=0; b=0;
     if (typeid(a) !=typeid(b))
     {
         cout << "aand b are of different types:\n";
         cout << "ais: " << typeid(a).name()<<'\n';
         cout << "bis: " << typeid(b).name()<<'\n';
     }
     return 0;
}

     执行结果:a and b are of different types:

a is: int *

b is: int

     当typeid应用到类,typeid用RTTI保持动态对象的类型的跟踪。当typeid应用到一个类型是多态类的表达式,结果是大多数继承完整对象类类型:

// typeid, polymorphic class
#include <iostream>
#include <typeinfo>
#include <exception>
using namespacestd;
class CBase{ virtual voidf(){} };
class CDerived: public CBase{};
int main() {
     try {
         CBase* a = new CBase;
         CBase* b = new CDerived;
         cout << "ais: " << typeid(a).name()<<'\n';
         cout << "bis: " << typeid(b).name()<<'\n';
         cout << "*ais: " << typeid(*a).name()<<'\n';
         cout << "*bis: " << typeid(*b).name()<<'\n';
     }catch (exception&e) {cout<< "Exception: " <<e.what()<< endl; }
     return 0;
}

    执行结果:a is: class CBase*

b is: class CBase *

*a is: class CBase

*b is: class CDerived

英文原文地址:http://www.cplusplus.com/doc/tutorial/typecasting/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值