将类型名作为强制类型转换运算符的做法是c的老式做法,c++为保持兼容而予以保留。
C++ 引入新的强制类型转换机制,主要是为了克服C语言强制类型转换的以下缺点:
转换太过随意,可以在任意类型之间转换。
没有统一的关键字和标示符。对于大型系统,做代码排查时容易遗漏和忽略
C++ 引入了四种功能不同的强制类型转换运算符以进行强制类型转换:static_cast、reinterpret_cast、const_cast 和 dynamic_cast,用法如下:
强制类型转换运算符 <要转换到的类型> (待转换的表达式)
static_cast
static_cast 用于进行比较“自然”和低风险的转换。
1.使用场景:
a、用于类层次结构中基类和派生类之间指针或引用的转换;
上行转换(派生类---->基类)是安全的。
下行转换(基类---->派生类)由于没有动态类型检查,所以是不安全的。
b、用于基本数据类型之间的转换,如把int转换为char,安全性问题由开发者来保证;
c、把空指针转换成目标类型的空指针;
d、把任何类型的表达式转为void类型;
2.使用特点
a、主要执行非多态的转换操作,用于代替C中通常的转换操作;
b、隐式转换都建议使用static_cast进行标明和替换;
示例:
#include < iostream>
using namespace std;
class A
{
public:
operator int() { return 1; }
operator char*() { return NULL; }
};
int main()
{
A a;
int n;
char* p = "New Dragon Inn";
n = static_cast (3.14); // n 的值变为 3
n = static_cast (a); // 调用 a.operator int,n 的值变为 1
p = static_cast (a); // 调用 a.operator char*,p 的值变为 NULL
n = static_cast (p); // 编译错误,static_cast不能将指针转换成整型
p = static_cast (n); // 编译错误,static_cast 不能将整型转换成指针
return 0;
}
reinterpret_cast
reinterpret_cast 用于进行各种不同类型的指针之间、不同类型的引用之间以及指针和能容纳指针的整数类型之间的转换。转换时,执行的是逐个比特复制的操作。
1.使用场景:
不到万不得已,不用使用这个转换符,高危操作;
2.使用特点:
a、reinterpret_cast是从底层对数据进行重新解释,依赖具体的平台,可移植性差;
b、reinterpret_cast可以将整型转换为指针,也可以把指针转换为数组;
c、reinterpret_cast可以在指针和引用里进行肆无忌惮的转换;
示例:
#include < iostream>
using namespace std;
class A
{
public:
int i;
int j;
A(int n):i(n),j(n) { }
};
int main()
{
A a(100);
int &r = reinterpret_cast(a); // 强行让 r 引用 a
r = 200; // 把 a.i 变成了 200
cout << a.i << "," << a.j << endl; // 输出 200,100 int n = 300; A *pa = reinterpret_cast (&n); // 强行让 pa 指向 n pa->i = 400; // n 变成 400
pa->j = 500; // 此条语句不安全,很可能导致程序崩溃
cout << n << endl; // 输出 400 long long la = 0x12345678abcdLL; pa = reinterpret_cast(la); // la太长,只取低32位0x5678abcd拷贝给pa unsigned int u = reinterpret_cast(pa); // pa逐个比特拷贝到u cout << hex << u << endl; // 输出 5678abcd typedef void (* PF1) (int); typedef int (* PF2) (int,char *); PF1 pf1; PF2 pf2; pf2 = reinterpret_cast(pf1); // 两个不同类型的函数指针之间可以互相转换 } 示例中的不安全代码:因为在编译器看来,pa->j 的存放位置就是 n 后面的 4 个字节。 本条语句会向这 4 个字节中写入 500。但这 4 个字节不知道是用来存放什么的,贸然向其中写入可能会导致程序错误甚至崩溃。
上面程序中的各种转换都没有实际意义,只是为了演示 reinteipret_cast 的用法而已。在编写黑客程序、病毒或反病毒程序时,也许会用到这样怪异的转换。
reinterpret_cast体现了 C++ 语言的设计思想:用户可以做任何操作,但要为自己的行为负责。
const_cast
const_cast 运算符仅用于进行去除 const 属性的转换,它也是四个强制类型转换运算符中唯一能够去除 const 属性的运算符。
1.使用场景:
a、常量指针转换为非常量指针,并且仍然指向原来的对象
b、常量引用被转换为非常量引用,并且仍然指向原来的对象
2.使用特点:
a、cosnt_cast是四种类型转换符中唯一可以对常量进行操作的转换符
b、去除常量性是一个危险的动作,尽量避免使用。
示例:
const string s = "Inception";
string& p = const_cast (s);
string* ps = const_cast (&s); // &s 的类型是 const string*
dynamic_cast
dynamic_cast专门用于将多态基类的指针或引用强制转换为派生类的指针或引用,而且能够检查转换的安全性。对于不安全的指针转换,转换结果返回 NULL 指针。
dynamic_cast 是通过“运行时类型检查”来保证安全性的。不能用于将非多态基类的指针或引用强制转换为派生类的指针或引用——这种转换没法保证安全性,只好用 reinterpret_cast 来完成。
1.使用场景:
只有在派生类之间转换时才使用dynamic_cast。
2.使用特点:
a、基类必须要有虚函数,因为dynamic_cast是运行时类型检查,需要运行时类型信息,而这个信息是存储在类的虚函数表中,只有一个类定义了虚函数,才会有虚函数表(如果一个类没有虚函数,那么一般意义上,这个类的设计者也不想它成为一个基类)。
b、对于下行转换,dynamic_cast是安全的(当类型不一致时,转换过来的是空指针),而static_cast是不安全的(当类型不一致时,转换过来的是错误意义的指针,可能造成踩内存,非法访问等各种问题)
c、dynamic_cast还可以进行交叉转换
示例:
#include < iostream>
#include < string>
using namespace std;
class Base
{
public:
virtual ~Base() {} //有虚函数,因此是多态基类
};
class Derived : public Base { };
int main()
{
Base b;
Derived d;
Derived* pd;
pd = reinterpret_cast (&b); // 此处pd不会为 NULL。reinterpret_cast不检查安全性,总是进行转换
if (pd == NULL)
cout << "unsafe reinterpret_cast" << endl; // 不会执行 pd = dynamic_cast (&b); // 结果会是NULL,因为 &b 不指向派生类对象,此转换不安全 if (pd == NULL) cout << "unsafe dynamic_cast1" << endl; // 会执行 pd = dynamic_cast (&d); // 安全的转换,此处 pd 不会为 NULL if (pd == NULL) cout << "unsafe dynamic_cast2" << endl; // 不会执行 return 0; }