在我们进行c++编程时候,有时候我们希望显示地将对象强制转换成另外一种类型。
c++为我们提供了4中强制类型转换:static_cast、dynamic_cast、const_cast和reinterpret_cast。
下面我们就来谈谈这4中强制类型转换的用法。
在c++ Primer一书中对static_cast是这样描述的:任何具有明确定义的类型转换,只有不包括底层const,都可以使用static_cast。
它主要有如下几种用法:
①用于类层次结构中基类和派生类之间指针或引用的转换。
进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;
进行下行转换(把基类指针或引用转换成派生类表示)是,由于没有没有动态类型检查,所以是不安全的。
②用于基本的数据类型的转换。
③把空指针转换成目标类型的空指针。
④把任何类型的表达式转换成void类型。
下面是static_cast使用的一些例子:
#include
using namespace std;
class CBase
{
public:
CBase() {}
virtual ~CBase() {}
};
class CDerived : public CBase
{
};
typedef struct rectangle
{
unsigned int uiLength;
unsigned int uiWide;
}RECTANGLE;
void Func(void *pParam)
{
//当对象有之间有"一定的关系"也可用static_cast转换
//例如:我们可以使用static_cast找回存在于void*指针中的值
RECTANGLE *pRectangle = static_cast(pParam);
cout << "Area = " << pRectangle->uiLength * pRectangle->uiWide << endl;
return;
}
int main(void)
{
//①用于类层次结构中基类和派生类之间指针或引用的转换。
CBase *pBase = new CBase();
CDerived *pDerived = new CDerived();
//进行上行转换,即派生类的指针或引用转换成基类
CDerived *pDerivedTmp = static_cast(pBase); //安全
//进行下行转换,即基类指针或引用转换成派生类
CBase *pBaseTmp = static_cast(pDerived); //不安全
delete pBase;
delete pDerived;
//②用于基本的数据类型的转换。
double dDoubleValue = 9.99;
int iIntValue = static_cast(dDoubleValue);
//③把空指针转换成目标类型的空指针。
int *pInt = static_cast(NULL);
//④把任何类型的表达式转换成void类型
RECTANGLE tRectangle;
tRectangle.uiLength = 5;
tRectangle.uiWide = 4;
Func(static_cast(&tRectangle));
return 0;
}
在c++ Primer一书中对const_cast是这样描述的:const_cast只能改变运算对象的底层cosnt。对于将常量对象转换成非常量对象的行为,我们一般称其为“去掉const性质”。一旦我们去掉了某个对象的const性质,编译器就不在阻止我们对该对象进行写操作了。如果对象本身不是一个常量,使用强制类型转换获得写权限是合法的行为。然而如果对象时一个常量,在使用const_cast执行写操作就会产生未定义的后果。
只有const_cast能改变表达式的常量属性,使用其他形式的命名强制类型转换改变表达式的常量属性都将引发编译器错误。同样的,也不能用const_cast改变表达式的类型。
下面是const_cast的一些例子:
const char *pConstChar = "const_cast";
//正确:但是通过pChar写值是未定义的行为
char *pChar = const_cast(pConstChar);
//错误:const_cast只改变常量属性,不能用const_cast改变表达式的类型
string strChar = const_cast(pConstChar);
在c++ Primer一书中对reinterpret_cast是这样描述的:reinterpret_cast通常为运算对象的位模式提供较低层次上的重新解释。注意:使用reinterpret_cast是非常危险的。
reinterpret_cast使用来处理无关类型之间的转换。下面给出一些IBM C++推荐的使用方式:
●A pointer to any integral type large enough to hold it (指针转向足够大的整数类型)
●A value of integral or enumeration type to a pointer (从整形或者enum枚举类型转换为指针)
●A pointer to a function to a pointer to a function of a different type (从指向函数的指针转向另一个不同类型的指向函数的指针)
●A pointer to an object to a pointer to an object of a different type (从一个指向对象的指针转向另一个不同类型的指向对象的指针)
●A pointer to a member to a pointer to a member of a different class or type, if the types of the members are both function types or object types (从一个指向成员的指针转向另一个指向类成员的指针或者是类型,如果类型的成员和函数都是函数类型或者对象类型)
下面是reinterpret_cast使用的一些例子:
#include
using namespace std;
typedef void(*FUNCPOINT1)(int);
typedef void(*FUNCPOINT2)(const char *);
enum eType {Type1, Type2};
class CClassA
{
public:
void func(void)
{
cout << "CClassA func" << endl;
};
};
class CClassB
{
public:
void func(void)
{
cout << "CClassB func" << endl;
};
};
typedef void(CClassA::*FUNCA)(void);
typedef void(CClassB::*FUNCB)(void);
void Func1(int i)
{
cout << i << endl;
}
int main(void)
{
//①从指针类型到一个足够大的整数类型
char *pChar = "reinterpret_cast";
int iPoint = reinterpret_cast(pChar);
//②从整数类型或者枚举类型到指针类型
int iValue = 0;
void *pVoid;
pVoid = reinterpret_cast(iValue);
enum eType eValue = Type1;
pVoid = reinterpret_cast(eValue);
//③从一个指向函数的指针到另一个不同类型的指向函数的指针
FUNCPOINT1 pFuncPoint1 = Func1;
FUNCPOINT2 pFuncPoint2 = reinterpret_cast(pFuncPoint1);
//④从一个指向对象的指针到另一个不同类型的指向对象的指针
CClassA *pClassA = new CClassA();
CClassB *pClassB = reinterpret_cast(pClassA);
delete pClassA;
//⑤从一个指向类函数成员的指针到另一个指向不同类型的函数成员的指针
FUNCA pFuncA = &CClassA::func;
FUNCB pFuncB = &CClassB::func;
CClassA CA;
CClassB CB;
(CA.*pFuncA)(); //输出"CClassA func"
(CB.*pFuncB)(); //输出"CClassB func"
FUNCA pFuncA = &CClassA::func;
FUNCB pFuncB = reinterpret_cast(pFuncA);
CClassA CA;
CClassB CB;
(CA.*pFuncA)(); //输出"CClassA func"
(CB.*pFuncB)(); //输出"CClassA func"
return 0;
}
dynamic_cast运算符,用于将基类的指针或引用安全地转换成派生类的指针或引用。
dynamic_cast运算符的使用形式如下所示:
dynamic_cast
dynamic_cast
dynamic_cast
其中,type必须是一个类类型,并且通常情况下改类型应该含有虚函数。在第一中形式中,e必须是一个有效的指针;在第二种形式中,e必须是一个左值;在第三种形式中,e不能是左值。
在上面的所有形式中,e的类型必须符合以下三个条件中的任意一个:e的类型是目标type的公有派生类、e的类型是目标type的公有基类或者e的类型就是目标type的类型。如果符合,则类型转换可以成功。否则,转换失败。如果一条dynamic_cast语句的转换目标是指针类型并且失败了,则结果为0。如果转换目标是引用类型并且失败了,则dynamic_cast运算符将抛出一个bad_cast异常。
在类的层次上进行上行的转换时,dynamic_cast和static_cast的效果是一样的。
在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
下面是dynamic_cast的使用例子:
#include "stdafx.h"
#include
using namespace std;
class CClassBase
{
public:
CClassBase() { };
virtual ~CClassBase() { };
virtual void func(void)
{
cout << "CClassBase virtual function" << endl;
}
};
class CClassDerived : public CClassBase
{
public:
CClassDerived() { };
~CClassDerived() { };
void func(void)
{
cout << "CClassDerived function" << endl;
}
};
int main(void)
{
CClassBase *pBase = new CClassBase();
CClassDerived *pDerivedStatic = static_cast(pBase);
CClassDerived *pDerivedDynamic = dynamic_cast(pBase);
if (pDerivedStatic == NULL)
cout << "pDerivedStatic == NULL" << endl;
else
cout << "pDerivedStatic != NULL" << endl;
if (pDerivedDynamic == NULL)
cout << "pDerivedDynamic == NULL" << endl;
else
cout << "pDerivedDynamic != NULL" << endl;
//在这里输出:
// pDerivedStatic != NULL
// pDerivedDynamic == NULL
//这就是在进行下行转换时,dynamic_cast具有类型检查的功能,而
//static_cast没有,因而dynamic_cast比static_cast更安全
return 0;
}