用类型名做强制类型转换运算符的做法,其实是C语言的老式做法,C++为了保持兼容而予以保留。在C++中,引入了4中功能不同的强制类型转换运算符以进行强制类型转换:static_castreinterpret_castconst_castdynamic_cast

强制类型转换是有一定风险的,有的转换并不一定安全。例如,把整数类型转换成指针,把基类转换成派生指针,把一种函数指针转换成另一种函数指针,把常量指针转换为非常量指针等,都是存在安全隐患的。C++引入新的强制类型转换,主要是为了克服C语言式的强制类型转换的3个缺点:

  • 没有从形式上体现出不同类型转换的功能和风险的不同。例如,将int强制转换为double是没风险的,将常量指针转换为非常量指针、把基类指针转换为派生类指针都是高风险的,而且后两者带来的风险不同,C语言的强制类型转换形式对这些不同并不加以区分。
  • 将多态基类指针转换成派生类指针时,不检查安全性,即无法判断转换后的指针是否确实指向一个派生类对象。
  • 难以在程序中寻找到底什么地方进行了强制类型转换。强制类型转换是引发程序运行时错误的一个原因,因此程序出错时,可能就会想查一下是不是有哪些强制类型转换出了问题。如果采用C语言的老式做法,要在程序中找出所有进行了强制类型转换的地方,显然是很麻烦的,因为这些转换没有统一的格式。而用C++的方式,则只需要查找 _cast字符串就够了。甚至可以根据错误的类型,有针对性地专门查找某一种强制类型转换。

C++强制类型转换运算符的用法:

1
2
3
强制类型转换运算符<要转换到的类型> (待转换的表达式)

double d = static_cast<double> (3 * 5);

static_cast

static_cast 用来进行比较自然和低风险的转换,如整型和实数型、字符型之间的互相转换。另外,如果对象所属的类重载了强制类型转换运算符T(如T是int,int* 或者别的什么类型名),那么static_cast也能用来进行对象到T类型的转换。

static_cast 不同用来在不同类型的指针之间互相转换(基类指针和派生类指针之间转换可以用到它),也不能用于整型和指针之间的互相转换,当然也不能用于不同类型的引用之间的转换。因为这些转换属于风险比较高的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#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;
}

reinterpret_cast

reinterpret_cast 用来进行各种不同类型的指针之间的转换、不同类型的引用之间的转换,以及指针和能容纳得下指针的整数类型之间的转换。转换的时候,执行的是逐个比特复制的操作。这种转换提供了很强的灵活性,但转换的安全性只能由程序员自己的细心来保证了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#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<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 (* PF1) (int);
	typedef int (* PF2) (int ,char*);
	PF1 pf1;
	PF2 pf2;
	pf2 = reinterpret_cast<PF2>(pf1);		//两个不同类型的函数指针之间可以相互转换
}

const_cast

此运算符仅仅用来进行去除const属性的转换,它也是4个强制类型转换运算符中唯一一个能够去除const属性的。它用于将const引用转换为同类型的非const引用,将const指针转换成同类型的非const指针。

1
2
3
const string s = "Inception";
string & p = const_cast<string &>(s);
string *ps = const_cast<string *>(&s);

dynamic_cast

用reinterpret_cast可以将多态基类(包含虚函数的基类)的指针强制转换为派生类的指针,但是这种转换不检查安全性,即不检查转换后的指针是否确实指向一个派生类对象。dynamic_cast专门用于将多态基类的指针或引用强制转换为派生类的指针或引用,而且能够检查转换的安全性。对于不安全的指针转换,转换结果返回NULL指针。

dynamic_cast是通过 运行时类型检查来保证安全性的。dynamic_cast不能用于将来非多态基类的指针或引用强制转换为派生类的指针或引用,这种转换没法保证安全性,只好用reinterpret_cast来完成。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#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_cast" << endl;
	pd = dynamic_cast<Derived*> (&d);	//安全的转换
	if (pd == NULL)						//此处pd不会为NULL
		cout << "unsafe reinterpret_cast" << endl;	
	return 0;
}

运行时类型检查

C++运算符 typeid是单目运算符,可以在程序运行过程中获取一个表达式的值的类型。typeid运算的返回值是一个 type_info类的对象,里面包含了类型的信息。type_info类是在头文件 typeinfo中定义的,一个type_info对象可以代表一种类型,它有成员函数 const char* name const,可以返回type_info对象所代表的类的名字。

两个type_info对象可以用 ==!=进行比较。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <typeinfo>
using namespace std;
struct Base {};
struct Derived: Base {};	//非多态基类
struct Poly_Base {virtual void Func(){} };	//多态基类
struct Poly_Derived: Poly_Base {};

int main()
{
	//基本类型
	long i;
	int * p = NULL;
	cout << "1) int is: " << typeid(int).name() << endl;	//输出1) int is: int
	cout << "2) i is: " << typeid(i).name() << endl;		//输出2) i is: long
	cout << "3) p is: " << typeid(p).name() << endl;		//输出3) p is: int *
	cout << "4) *p is: " << typeid(*p).name() << endl; 	//输出4) *p is: int

	//非多态类型
	Derived derived;
	Base* pbase = &derived;
	cout << "5) derived is: " << typeid(derived).name() << endl; //输出5) derived is: struct Derived
	cout << "6) *pbase is: " << typeid(*pbase).name() << endl; //输出6) *pbase is: struct Base
	cout << "7) " << (typeid(derived) == typeid(*pbase)) << endl;	//输出7) 0

	//多态基类
	Poly_Derived polyderived;
	Poly_Base* ppolybase = &polyderived;
	cout << "8) polyderived is: " << typeid(polyderived).name() << endl;	//输出struct Poly_Derived
	cout << "9) *ppolybase is: " << typeid(*ppolybase).name() << endl;		//输出struct Poly_Derived
	cout << "10) " << (typeid(polyderived) != typeid(*ppolybase)) << endl;	//输出10) 0
}