C语言里将类型符号作为强制类型转换运算符的做法在C++中得到了兼容,但是C++中引入了四种功能不同的强制类型转换运算符:static_cast、reinterpret_cast、const_cast、dynamic_cast
1.为什么要新增四种强制类型转换?
强制类型转换具有一定风险,如:把整型转换为指针类型、将基类指针转换为派生类指针、将一种函数指针转换为另一种函数指针,将常量指针转换为非常量指针等。C++新引用四种强制类型转换主要是为了规避这种风险:
a. 将int转换为double是没有风险的,而将常量指针转换为非常量指针,将int转换为指针类型都是高风险的,C中并未对这两种情况作区分
b. 将多态的基类指针转换为派生类指针时并不检查安全性,无法判断转换后的指针是否指向一个派生类对象
c. 如果强制类型转换出错,很难排查出错误
2.C++中强制类型转换运算符的用法
强制类型转换运算符<要转换的类型>(待转换的表达式)
如:double num = static_cast (1+2); // 将1+2转换为浮点类型
3.static_cast
static_cast主要用于“低风险”的类型转换,如整数、浮点数和字符类型的转换,如果对象重载了强制类型转换运算符T(T是int、char或其他类型),则static_cast也可以用于将对象转换为T类型;而像整型和指针这种“高风险”的转换,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 <int> (3.14); // n 的值变为 3
n = static_cast <int> (a); //调用 a.operator int,n 的值变为 1
p = static_cast <char*> (a); //调用 a.operator char*,p 的值变为 NULL
n = static_cast <int> (p); //编译错误,static_cast不能将指针转换成整型
p = static_cast <char*> (n); //编译错误,static_cast 不能将整型转换成指针
return 0;
}
4.reinterpret_cast
前面说到,“高风险”的类型转换无法用static_cast解决,因此,C++中使用reinterpret_cast解决这种转换问题,在使用reinterpret_cast时,使用的是逐个比特位复制的操作,它支持各种不同类型的指针之间、不同类型的引用之间以及指针和能容纳指针的整数之间的转换,reinterpret_cast提供了很高的灵活性,但是这种转换的后果却不保证安全性,因此要慎用。
#include <iostream>
using namespace std;
class A
{
public:
int i;
int j;
A(int n):i(n),j(n) { }
};
bool func(int x)
{
cout << "调用了func函数,其类型为 bool (*func)(int)" << endl;
}
int main()
{
A a(100); // a.i = a.j = 100
int &r = reinterpret_cast<int&>(a); //强行让 r 引用 a
r = 200; //把 a.i 变成了 200
cout << a.i << "," << a.j << endl; // 输出 200,100
int n = 300;
A *pa = reinterpret_cast<A*> ( & n); //强行让 pa 指向 n
pa->i = 400; // n 变成 400
pa->j = 500; //此条语句不安全,很可能导致程序崩溃
cout << n << endl; // 输出 400
long long la = 0x12345678abcdLL;
pa = reinterpret_cast<A*>(la); //la太长,只取低32位0x5678abcd拷贝给pa
unsigned int u = reinterpret_cast<unsigned int>(pa);//pa逐个比特拷贝到u
cout << hex << u << endl; //输出 5678abcd
typedef void (* Test) ();
Test test = reinterpret_cast<Test>(func);
test(); // 将int(*)(int)的类型当做void(*)()来使用,程序可以运行,但是不保证安全性,可能会产生不可预料的结果
}
5.const_cast
const_cast运算符主要是为了去除const属性,它是四种强制类型转换运算符中唯一一个可以去除const属性的运算符
const string str = "This is a test.";
string& rs = const_cast<string&>(str);
string* ps = const_cast<string*>(&str);
// &str的类型是,const string*;
6.dynamic_cast
使用reinterpret_cast用于将多态基类指针强制转换为派生类指针,但是这种转换不保证安全性,即不检查转换后的指针是否指向一个派生类对象,dynamic_cast专门用于将多态基类类型的指针强制转换为派生类类型的指针,而且能够检查转换的安全性,对于不安全的转换,返回一个nullptr
dynamic_cast是通过“运行时类型检查”来保证安全性的。但是,dynamic_cast 不能用于将非多态基类的指针或引用强制转换为派生类的指针或引用—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 <Derived*> (&b);
if (pd == NULL)
//此处pd不会为 NULL。reinterpret_cast不检查安全性,总是进行转换
cout << "unsafe reinterpret_cast" << endl; //不会执行
pd = dynamic_cast <Derived*> (&b);
if (pd == NULL) //结果会是NULL,因为 &b 不指向派生类对象,此转换不安全
cout << "unsafe dynamic_cast1" << endl; //会执行
pd = dynamic_cast <Derived*> (&d); //安全的转换
if (pd == NULL) //此处 pd 不会为 NULL
cout << "unsafe dynamic_cast2" << endl; //不会执行
return 0;
}
特殊情况
如果 Derived & r = dynamic_cast <Derived &> (b);
那该如何判断该转换是否安全呢?不存在空引用,因此不能通过返回值来判断转换是否安全。
dynamic_cast 在进行引用的强制转换时,如果发现转换不安全,就会拋出一个异常,通过处理异常,就能发现不安全的转换。
7.explict 关键字
explict的作用是阻止经过转换构造函数进行的隐式转换的生成
#include <iostream>
using namespace std;
class A
{
public:
explicit A(int n)
{
cout << "A(int)调用" << endl;
}
A(const A& a)
{
cout << "A(const A&)调用" << endl;
}
private:
int _a;
};
int main()
{
A a(10);
A cpy = 10; // 先以10构造一个临时对象,然后将临时对象赋值给cpy, A tmp(10) -> A cpy = tmp 两步过程
return 0;
}