一、C风格强制转换
C风格的转型动作主要有如下两种形式:
(T)expression //将expression转型为T
T(expression) //将expression转型为T(函数风格)
这两种形式并无差别,只是小括号摆放位置不同而已,这两种形式为旧式转型(old-style casts)。
C++还提供了四种新式转型,常常被称为new-style或C++-style casts:
const_cast(expression)
static_cast(expression)
dynamic_cast(expression)
reinterpret_cast(expression)
这四种方式各有不同目的,下面详细解说。
二、const_cast
const_cast通常被用来将对象的常量性转除(cast away the constness),它也是唯一有此能力的C++-style转型操作符。简单来说就是去掉const属性,比如:
const int i = 1;
int j = const_cast(i);
三、static_cast
static_cast用来强迫隐式转换(implicit conversions),简单来说就是基本类型转换,比如:
int i = 2;
double d = static_cast(i); //基本类型转换
但是需要注意一下几点:
(1)子类指针转换为父类指针是安全的,但是父类指针转换为子类指针时不安全的(建议用dynamic_cast),具体例子见后文。
(2)不能进行无关类型指针之间的转换,即非父类和子类之间的转换,但可以将任何类型的指针转换成void类型指针,比如:
int i = 3;
int *pi = &i;
double *pd = static_cast(&i); //无关类型指针转换,编译错误
void *pv = static_cast(pi); //任意类型转换成void类型
四、dynamic_cast
dynamic_cast主要用来执行“安全向下转型”(safe downcasting),也就是用来决定某对象是否归属继承体系中的某个类型,它是唯一无法由旧式语法执行的动作,也是唯一可能耗费重大运行成本的转型动作。简单来说就是多态类(父子类)之间的类型转换,比如:
class Base {
public:
int m_iTest;
virtual void TestFunc() {}; //基类必须有虚函数,保持多态性才能使用dynamic_cast
};
class Derived : public Base {
public:
char *m_szTest[256];
void TestSon() {};
};
Base *pb1 = new Derived();
Derived *pd1 = static_cast(pb1); //子类转换为父类,静态类型转换,正确但不推荐
Derived *pd2 = dynamic_cast(pb1); //子类转换为父类,动态类型转换,正确
Base *pb2 = new Base();
Derived *pd3 = static_cast(pb2); //父类转换为子类,静态类型转换,不安全,由于切割导致访问子类成员变量时越界
Derived *pd4 = dynamic_cast(pb2); //父类转换为子类,动态类型转换,安全,结果为NULL
需要注意的是:首先,要保证基类有虚函数即有多态性才能使用dynamic_cast;其次,要判断转换结果是否为NULL,否则后果自负。
五、reinterpret_cast
reinterpret_cast意图执行低级转型,实际动作及结果可能取决于编译器,这也就表示其不可移植。简单来说就是不同类型的指针类型转换,具体如下:
(1)转换的类型必须是一个指针、引用、函数指针或成员指针。
(2)在比特位级别进行转换,即可以将一个指针转换成一个整数,也可以把一个整数转换成一个指针(即先把一个指针转换成一个整数,再把该整数转换成原类型的指针,还可以得到原先的指针值),但不能将非32 bit(具体位数跟平台有关,也可能是64 bit)的实例转换成指针。
具体例子如下:
int doSomething() {return 0;}
typdef void (*FuncPtr)(); //函数指针类型
FuncPtr funcPtrArray[256]; //函数指针数组
funcPtrArray[0] = &doSomething; //编译错误,类型不匹配
funcPtrArray[0] = reinterpret_cast(&doSomething); //不同函数指针类型之间强制转换
六、总结
1、新旧转型选择
旧式转型任然合法,但新式转型较受欢迎,原因有二:
(1)它们很容易在代码中被辨识出来(不论是人工辨识或使用工具如grep),因而得以简化“找出类型系统在哪个地点被破坏”的过程。
(2)各转型动作的目标愈窄化,编译器愈可能诊断出错误的运用。
2、转型副作用
无论哪种类型的转型都会带来性能的损失,因此:
(1)如果可以,尽量避免转型,特别是在注重效率的代码中避免dynamic_cast,如果有个设计需要转型动作,试着发展无需转型的替代设计。
(2)如果转型是必要的,试着将它隐藏于某个函数背后,调用者随后可以调用该函数,而不需将转型放进它们自己的代码内。
阅读(2289) | 评论(1) | 转发(0) |