隐式类型转换是安全的,显式类型转换是有风险的

C风格的强制类型转换统一使用( ),而( )在代码中随处可见,所以也不利于使用文本检索工具

 
 

有几种特定于c++语言的类型转换操作符。这些操作符旨在消除旧式C语言强制类型转换中固有的一些模糊性和危险

 
 

c++类型转换的几种分类

关键字

说明

static_cast

用于良性转换,一般不会导致意外发生,风险很低。用于非多态的转换。

const_cast

用于 const 与非 const、volatile 与非 volatile 之间的转换。

reinterpret_cast

高度危险的转换,这种转换仅仅是对二进制位的重新解释,不会借助已有的转换规则对数据进行调整,但是可以实现最灵活的 C++ 类型转换。

dynamic_cast

借助 RTTI,用于类型安全的向下转型(Downcasting)。
用于多态类型的转换。

safe_cast

Used in C++/CLI to produce verifiable MSIL.

 
 

 
 

来自 <http://c.biancheng.net/cpp/biancheng/view/3297.html>

 
 

dynamic_cast Operator

 
 

语法

dynamic_cast < type-id > ( expression )

 
 

type-id 必须是一个指针或引用指向之前定义好的类类型,或者是一个指向void的指针

如果type-id是指针 expression也得是指针

如果type-id是引用 expression需要是左值

 
 

当type-id是指向值类型的内部指针时,动态强制转换将不再抛出异常,而强制转换在运行时失败。强制转换将返回0指针值,而不是抛出。

 
 

如果type-id是一个指向明确可访问的表达式直接或间接基类的指针,那么结果就是指向type-id类型的惟一子对象的指针。

比如:

class B { };

class C : public B { };

class D : public C { };

 
 

void f(D* pd) {

C* pc = dynamic_cast<C*>(pd); // ok: C是一个直接基类

// pc指向pd的C子对象

B* pb = dynamic_cast<B*>(pd); // ok: B是D的间接基类

// pb指向pd的B子对象

}

 
 

 
 

dynamic_cast与static_cast是相对的

 
 

dynamic_cast 用于在类的继承层次之间进行类型转换,它既允许向上转型(Upcasting),也允许向下转型(Downcasting)。向上转型是无条件的,不会进行任何检测,所以都能成功;向下转型的前提必须是安全的,要借助 RTTI 进行检测,所有只有一部分能成功。

 
 

static_cast Operator

 
 

语法

static_cast <type-id> ( expression )

 
 

在标准c++中,不进行任何运行时类型检查来帮助确保转换的安全性。

该运算符可以用于将基类指针转换为派生类指针等操作,这样的转换并不总是安全的。该操作符只能用于良性转化,比如:

  • 原有的自动类型转换,例如 short 转 int、int 转 double、const 转非 const、向上转型等
  • void 指针和具体类型指针之间的转换,例如void *转int *、char *转void *等
  • 有转换构造函数或者类型转换函数的类与其它类型之间的转换,例如 double 转 Complex(调用转换构造函数)、Complex 转 double(调用类型转换函数)。

 
 

静态和动态的区别

一般情况下 静态操作符可以转换数值类数据类型,比如enums转ints 转floats。静态强制转换没有动态强制转换安全,因为静态强制转换没有运行时类型检查。对于不明确的指针的动态强制转换会失败,但是静态强制转换会不检查就返回,仿佛没有错误。但是动态强制转换只能用于指针和引用,并且运行时类型检查时有一定开销的。

下面有一个例子:

class B {};

 
 

class D : public B {};

 
 

void f(B* pb, D* pd) {

D* pd2 = static_cast<D*>(pb); // 不安全, 将B类型强制转换为D类型,D中可能有B没有的字段和方法

B* pb2 = static_cast<B*>(pd); // 安全,因为派生类D一定有基类B的字段和方法。

}

 
 

动态强制转换和静态强制转换操作符在类层次结构中移动指针。但是,静态强制转换仅依赖于cast语句中提供的信息,因此可能不安全。例如:

class B {

public:

virtual void Test(){}

};

class D : public B {};

 
 

void f(B* pb) {

D* pd1 = dynamic_cast<D*>(pb);

D* pd2 = static_cast<D*>(pb);

}

如果pb确实指向类型为D的对象,那么pd1和pd2将获得相同的值。如果pb == 0,它们也会得到相同的值。

如果pb指向的是B类型的对象,而不是完整的D类,那么动态强制转换将知道足够多的信息以返回0。但是,静态强制转换依赖于程序员的断言,即pb指向类型为D的对象,并且只返回一个指向假定的D对象的指针。

因此,静态强制转换可以执行隐式转换的逆转换,在这种情况下,结果是未定义的。由程序员来验证静态强制转换的结果是否安全。

这种行为也适用于类类型之外的类型。例如,可以使用静态强制转换将int转换为char。但是,得到的char可能没有足够的位来容纳整个int值。同样,由程序员来验证静态强制转换的结果是否安全。

静态强制转换操作符还可用于执行任何隐式转换,包括标准转换和用户定义转换。例如

typedef unsigned char BYTE;

 
 

void f() {

char ch;

int i = 65;

float f = 2.5;

double dbl;

 
 

ch = static_cast<char>(i); // int 转 char

dbl = static_cast<double>(f); // float转 double

i = static_cast<BYTE>(ch);

}

任何表达式都可以通过静态强制转换操作符显式转换为void类型。目标void类型可以选择包含const、volatile或unaligned属性。

静态强制转换操作符不能丢弃常量、易失性或未对齐的属性。

需要注意的是,static_cast 不能用于无关类型之间的转换,因为这些转换都是有风险的:

  • 两个具体类型指针之间的转换,例如int *转double *、Student *转int *等。不同类型的数据存储格式不一样,长度也不一样,用 A 类型的指针指向 B 类型的数据后,会按照 A 类型的方式来处理数据:如果是读取操作,可能会得到一堆没有意义的值;如果是写入操作,可能会使 B 类型的数据遭到破坏,当再次以 B 类型的方式读取数据时会得到一堆没有意义的值。
  • int 和指针之间的转换。将一个具体的地址赋值给指针变量是非常危险的,因为该地址上的内存可能没有分配,也可能没有读写权限,恰好是可用内存反而是小概率事件。

 
 

reinterpret_cast Operator

 
 

语法

reinterpret_cast < type-id > ( expression )

 
 

这个运算符用不好会造成不安全,尽量能用其他的代替就用其他的。

这种转换仅仅是对二进制位的重新解释,不会借助已有的转换规则对数据进行调整,所以风险很高。

reinterpret cast操作符可用于将char*转换为int*,或将一个类*转换为不相关的类*,这些操作符本质上是不安全的。

重新解释强制类型转换的结果除了被强制转换回其原始类型外,不能安全地用于其他任何事情。