以下内容参考北京大学郭炜老师mooc《程序设计与算法(三)C++面向对象程序设计》
有一个问题就是,明明可以用以下的方式来进行强制类型转换,为什么还要用C++提供的四种类型转换呢?
float a = 1.0;
int b = (int)a;
那是因为原来的类型转换不能区分转换风险的高低, 比如指针转换就用interpret_cast
或dynamic_cast
。这样可以对转换的形式和风险程度分级,这样可以很容易地在程序中搜寻我们在程序中哪里进行了某一类的转换
static_cast
static_cast用来进行比较”自然“和低风险的转换,比如整形和实数型、字符型之间的互相转换。什么叫低风险的转换呢?我们要知道,类型转换是有风险的,比如把浮点数转换为整型数,就有丢失精度的风险;如果把一个基类的指针强制转换成派生类的指针,然后使用这个派生类的指针,实际上指向的是一个基类的对象,如果把它当派生类的指针来用的话,可能调用的就是基类的成员函数,这个时候就会出错
static_cast不能用来在不同类型的指针之间互相转换,也不能用于整型和指针之间的互相转换,也不能用于不同类型的引用之间的转换。
#include <iostream>
using namespace std;
class A
{
public:
operator int() { return 1; } //类型强制转换运算符重载,这是不写返回值类型的,重载是什么类型,返回值就是什么类型。
operator char*() { return "who r u"; }
};
int main()
{
A a;
int n;
char *p = "I am zzy";
n = static_cast<int>(3.14);
cout << n << endl;
n = static_cast<int>(a);//调用a.operator int, n的值变为1
cout << n << endl;
p = static_cast<char *>(a); //调用a.operator char*, p的值变为"who r u"
cout << p << endl;
n = static_cast<int>(p);//编译错误,static_cast不能将指针转换成整型
p = static_cast<int>(n);//编译错误,static_cast不能将整型转换成指针
return 0;
}
reinterpret_cast
reinterpret_cast
用来进行不同类型指针之间的转换,不同类型的引用之间的转换、以及指针和能容纳得下指针的整数类型之间的转换(比如指针是4个字节,那么只能转换成int及以上的,因为int也是四个字节)。转换的时候,执行的是逐个比特拷贝的操作。
#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;
int n = 300;
A *pa = reinterpret_cast<A *>(&n);//强行让A类的指针pa指向n
pa->i = 400;//n变成400
pa->j = 500;//词条语句不安全,很可能导致程序崩溃
cout << n << endl;
long long la = 0x12345678abcdLL;
pa = reinterpret_cast<A *>(la);//la太长,只取低32位拷贝给pa
unsigned int u = reinterpret_cast<unsigned int>(pa);//pa逐个拷贝给u
cout << hex << u << endl;//输出5678abcd
}
上面这个例子,让r强行引用了a,然后将r赋值为200,就会在r这引用的4个字节里面写入200,而A在内存中占8个字节,前4个是i,后4个是j,而r引用的就是前4个字节也就是i的内存。
而后面让A类的指针pa指向了整型变量n,因为n占4个字节,所以pa->i
刚好指向n的内存空间,而pa->j
指向n之后的4个字节,就不知道是什么地方了,可能是其他变量的内存空间,甚至可能是根本不允许写的地方,所以可能导致程序崩溃。
const_cast
用来进行去除const属性的转换。将const引用转换成同类型的非const引用,将const指针转换乘同类型的非const指针。
const string s = "zzy";
string &p = const_cast<string&>(s);
string *ps = const_cast<string*>(&s);//&s的类型是const string*
dynamic_cast
我们经常会做从基类指针到派生类指针的转换,基类指针到派生类指针是需要强制转换的,这个转换的安全性就取决于基类指针是不是确实指向一个派生类的对象。
总而言之,基类指针到派生类指针的强制转换,只有在基类指针指向派生类的对象时才是安全的。
dynamic_cast专门用于将多态基类的指针或引用,强制转换成派生类的指针或引用,而且能够检查转换的安全性。对于不安全的指针转换,转换结果将返回NULL指针。
dynamic_cast不能用于将非多态基类的指针或引用,强制转换成派生类的指针或引用。
#include <iostream>
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)//pd为NULL,因为&b不是只想派生类对象,此转换不安全
cout << "unsafe dynamic_cast" << endl;
Base *pb = &d;
pd = dynamic_cast<Derived *>(pb);//安全的转换
if(pd==NULL)//不为NULL
cout << "unsafe dynamic_cast2" << endl;
system("pause");
}