显式类型转换函数
一、C 风格(C-style)强制转型如下:
(T) expression // cast expression to be of type T
函数风格(Function-style)强制转型使用这样的语法:
T(expression) // cast expression to be of type T
这两种形式之间没有本质上的不同,它纯粹就是一个把括号放在哪的问题。我把这两种形式称为旧风格(old-style)的强制转型
二、C++ style
旧风格的强制转型依然合法,但是新的形式更可取。首先,在代码中它们更容易识别(无论是人还是像 grep 这样的工具都是如此),这样就简化了在代码中寻找类型系统被破坏的地方的过程。第二,更精确地指定每一个强制转型的目的,使得编译器诊断使用错误成为可能。例如,如果你试图使用一个 const_cast 以外的新风格强制转型来消除常量性,你的代码将无法编译。
2.1 四个函数概述
C++的四种强制转型形式:
dynamic_cast:动态类型转换
static_cast:静态类型转换
reinterpret_cast:重新解释类型转换
const_cast:常量类型转换
static_cast
用法:static_cast < type > ( expression )
该运算符把expression转换为type类型,但没有运行时类型检查来保证转换的安全性。
一般是普通数据类型(如int m=static_cast
static_cast 可以被用于强制隐型转换(例如,non-const 对象转型为 const 对象,int 转型为 double,等等),它还可以用于很多这样的转换的反向转换(例如,void* 指针转型为有类型指针,基类指针转型为派生类指针),但是它不能将一个 const 对象转型为 non-const 对象(只有 const_cast 能做到),它最接近于C-style的转换。
它主要有如下几种用法:
①用于类层次结构中基类和子类之间指针或引用的转换。
进行上行转换(把子类的指针或引用转换成基类表示)是安全的;
进行下行转换(把基类指针或引用转换成子类表示)时,由于没有动态类型检查,所以是不安全的。
class B { ... };
class D : public B { ... };
void f(B* pb, D* pd)
{
D* pd2 = static_cast
B* pb2 = static_cast(pd); // 安全的
}
②用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
③把空指针转换成目标类型的空指针。
④把任何类型的表达式转换成void类型。
注意:static_cast不能转换掉expression的const、volitale、或者__unaligned属性。
static_cast允许执行任意的隐式转换和相反转换动作。(即使它是不允许隐式的)
应用到类的指针上,意思是说它允许子类类型的指针转换为父类类型的指针(这是一个有效的隐式转换),同时,也能够执行相反动作:转换父类为它的子类。
在这例子里,被转换的父类没有被检查是否与目的类型相一致。
class Base {};
class Derived : public Base {};
Base *a = new Base;
Derived *b = static_cast
'static_cast'
double d = 3.14159265;
int i = static_cast
int i=0;
double d = static_cast
int j = static_cast
还有:
class A
{
protected:
int m_x;
char *m_username;
public :
A(int x) { m_x=x; }
A(char *username)
{
m_username=new char[strlen(username)+1];
strcpy(m_username, username);
}
operater int () { return m_x; }
operator char *() const { return m_username; }
}
A a(2);
int x=static_cast
char *p=static_cast
int *px;
double y=2.2;
px=static_cast
//
dynamic_cast
一般用在父类和子类指针或引用的互相转化,如果启动了支持运行进类型信息RTTI)可以有助于判断在运行时所指向的对象的确切类型。注:vc++默认式禁止RTTI信息的,若开启,需在工程-》设置,在 c++选项中,选c++语言,勾上“允许运行时类型信息RTTI".
用法:dynamic_cast < type-id > ( expression )
该运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void *;如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。
dynamic_cast 主要用于执行“安全的向下转型(safe downcasting)”,也就是说,要确定一个对象是否是一个继承体系中的一个特定类型。它是唯一不能用旧风格语法执行的强制转型,也是唯一可能有重大运行时代价的强制转型。
dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;
在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
class B{
public:
int m_iNum;
virtual void foo();
};
class D:public B{
public:
char *m_szName[100];
void print() { cout << "D:print()" << endl; };
};
void func(B *pb){
D *pd1 = static_cast(pb);
D *pd2 = dynamic_cast(pb);
pd2->print();
pd2->m_szName[0]=’a’; // error
pd1->m_szName[0]=’a’; //right;
}
在上面的代码段中,如果pb指向一个D类型的对象,pd1和pd2是一样的,并且对这两个指针执行D类型的任何操作都是安全的;
但是,如果pb指向的是一个B类型的对象,那么pd1将是一个指向该对象的指针,对它进行D类型的操作将是不安全的(如访问m_szName),而pd2将是一个空指针(检测在运行时进行,如果被转换的指针不是一个被请求的有效完整的对象指针,返回值为NULL。表达式dynamic_cast
另外要注意:B要有虚函数,否则会编译出错;static_cast则没有这个限制。这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表中,只有定义了虚函数的类才有虚函数表,没有定义虚函数的类是没有虚函数表的。
'dynamic_cast'只用于对象的指针和引用。当用于多态类型时,它允许任意的隐式类型转换以及相反过程。不过,与static_cast不同,在后一种情况里(注:即隐式转换的相反过程)。static_cast通常可用于类层次的静态导航,无映射变换,窄化变换(会丢失信息)等等,static_cast的应用要广一些,但如前所提到的,在类层次导航变换中我们应该使用前者,因为后者static_cast可能意味着冒险(比如变换时类似于强制转换一样丢失信息)。在某些情况下较dynamic_cast快.
另外,dynamic_cast还支持交叉转换(cross cast)。如下代码所示。
class A{
public:
int m_iNum;
virtual void f(){}
};
class B:public A{ };
class D:public A{ };
B *pb = new B;
pb->m_iNum = 100;
D *pd1 = static_cast(pb); //compile error
D *pd2 = dynamic_cast(pb); //pd2 is NULL
delete pb;
使用static_cast进行转换是不被允许的,将在编译时出错;而使用 dynamic_cast的转换则是允许的,结果是空指针。
reinterpret_cast
很像c的一般类型转换操作,是最危险的,只要编译通过就行,使用不当的话很有可能导致RUN-TIME大灾难. 是强制转换,而不论是否提供了相应构造函数或转换类型数,因此,在转换时其内存格式并不变化,例如,当对double转换成int后,通过cout输出时,输出的仍然是double类型,其主要目的是:将一个指针转换成其它类型的指针。这个操作符能够在非相关的类型之间转换。操作结果只是简单的从一个指针到别的指针的值的二进制拷贝。在类型之间指向的内容不做任何类型的检查和转换。
reinterpret_cast是特意用于底层的强制转型,导致实现依赖(implementation-dependent)(就是说,不可移植)的结果,例如,将一个指针转型为一个整数。这样的强制转型在底层代码以外应该极为罕见。
class A {};
class B {};
A * a = new A;
B * b = reinterpret_cast(a);
'reinterpret_cast'就像传统的类型转换一样对待所有指针的类型转换。
const_cast
是把cosnt或volatile属性去掉,const_cast 一般用于强制消除对象的常量性。去除常量变量的常量属性,或反之将非常量的指针变量转换成常指针变量。
const int x=12;
int *px=x; //syntax error, int pointer can not point to const int
可以改为:
const int x=12;
int *px=const_cast
*px=20;//ok
又例如:
class Account
{
int balance;
public:
Account(int b) { balance=b; }
void operater +=(int newbalance) { balance+=newbalance; }
}
const Account a(9000);
Account *pa=&a;//syntax error!
*pa+=1000;//syntax error!
可以改为:
const Account a(9000);
Account *pa=const_cast
*pa+=1000;
总的来说,const_cast的主要作用之一是去掉const保护。因此少用为好。