C风格的类型转换很容易理解:
(目标类型)表达式
C++认为C风格的类型转换过于松散,可能会带来隐患,不够安全,于是C++推出了新的类型转换来代替,采用更严格的语法检查,降低使用风险。
C++新增了四个关键字static_cast、const_cast、reinterpret_cast和dynamic_cast,用于c++风格的类型转换。
注意:C++的类型转换只是语法上的解释,本质上与C风格的类型转换没有什么区别,C语言做不到的事情C++也做不到。
<1>static_cast(实际开发用的最多)
1.语法 static<目标类型>(表达式)
(1)用于内置数据类型之间的转换。
总结:用于内置数据类型之间转换时,效果和C风格强制转换一致。
(2)用于指针类型之间的转换。
总结:用于指针类型转换时,需要先将某类型指针隐式类型转换成void*类型,才能通过stacit_cast转换成第三种类型。(这种模式主要用在函数传参的过程中,形参设置为void*,实参是各种指针,函数体内static_cast转换)。实际上在同一函数内部进行指针类型来回转换的场景存在吗?
static_cast()测试代码
#include<iostream>
using namespace std;
//1 用于内置数据类型之间的转换
void test01()
{
int ii = 10;
long ll = ii; //绝对安全,可以隐式转换,不会出现警告
double dd = 1.23;
long ll1 = dd;//可以隐式转换,但是,会出现可能丢失数据的警告。
long ll2 = (long)dd;//C风格: 显示转换,不会出现警告
long ll3 = static_cast<long>(dd);//C++风格:显示转换,不会出现警告
cout << ii << " " << ll << " " << dd << " " << ll1 << " " << ll2 <<" "<<ll3<< endl;
}
//2 用于指针之间的转换
void func(void* ptr)//形参设计成void*最合理方便
{
double* pp = static_cast<double*>(ptr);
}
void test02()
{
int ii = 10;
//double* pd1 = ⅈ //错误,不能隐式类型转换
double* pd2 = (double*)ⅈ //正确 C风格,类型强转
//double* pd3 = static_cast<double*>(&ii);//错误,static_cast不支持不同类型指针的转换
void* pv = ⅈ //任何类型的指针都可以隐式转换成void*
double* pd4 = static_cast<double*>(pv);//static_cast可以把void*类型指针转换为其他类型指针
func(&ii);//int*直接传参,在函数中实现int*->void*->double*
}
int main()
{
//void test01();
void test02();
system("pause");
return 0;
}
<2> dynamic_cast
1.运行阶段类型识别(RTTI),为程序在运行阶段确定对象的类别,只适用于包含虚函数的类。
2.基类指针可以指向派生类对象,如何知道基类指针指向的是哪种派生类对象呢(想调用派生类中的非虚函数)。
3.dynamic_cast运算符用基类类型的指针来生成派生类类型的指针,它不能回答指针指向的是什么类型对象的问题,但是可以回答是否可以安全地将对象的地址赋值给特定类型的指针的问题。
4.语法:派生类指针 = dynamic_cast<派生类类型*>(基类指针);
5.如果转换成功,dynamic_cast返回对象的地址,如果失败,返回nullptr。
注意:
1.dynamic_cast只适用于包含虚函数的类(当基类不包含虚函数时,使用dynamic_cast会报错,因为dynamic_cast只为多态的应用场景设计,使用时要查虚函数表,如果不是多态类型,编译过不去)。
2.dynamic_cast可以将派生类指针转换为基类指针,但是这种画蛇添足的做法没有意义。(注意派生类指针可以直接赋值给基类指针,不需要任何转换)。
3.dynamic_cast可以用于引用,但是不存在与空指针对应的引用值,如果转换请求不正确,会出现bad_cast异常。
dynamic_cast测试代码
#include<iostream>
using namespace std;
class Hero
{
public:
int viabilty; //生存能力
int attack; //攻击伤害
virtual void skill1() { cout << "英雄释放了第一技能" << endl; }//父类不包含虚函数时,使用dynamic_cast直接报错
virtual void skill2() { cout << "英雄释放了第二技能" << endl; }
virtual void uskill() { cout << "英雄释放了大招" << endl; }
};
class XS :public Hero
{
public:
void skill1() { cout << "西施释放了第一技能" << endl; }
void skill2() { cout << "西施释放了第二技能" << endl; }
void uskill() { cout << "西施释放了大招" << endl; }
void show() { cout << "我是天下第一美女" << endl; }
};
class HX :public Hero
{
public:
void skill1() { cout << "韩信释放了第一技能" << endl; }
void skill2() { cout << "韩信释放了第二技能" << endl; }
void uskill() { cout << "韩信释放了大招" << endl; }
};
class LB :public Hero
{
public:
void skill1() { cout << "李白释放了第一技能" << endl; }
void skill2() { cout << "李白释放了第二技能" << endl; }
void uskill() { cout << "李白释放了大招" << endl; }
};
//void doSkill(Hero* ho)
//{
// ho->skill1();
// ho->skill2();
// ho->uskill();
//}
//引用演示
void test01()
{
LB lb;
//const Hero& xs_ref = nullptr;//c++中引用的目标不能是空指针
Hero& lb_ref = lb;
XS& xs_ref = dynamic_cast<XS&>(lb_ref);//此处转换有问题会抛出bad_cast异常
}
int main()
{
cout<< "---------请选择你的英雄---------"<< endl;
cout<< "----1.西施 2.韩信 3.李白------"<< endl;
Hero* ho = NULL;
int select = 0;
cin >> select;
switch (select)
{
case 1:
{
ho = new XS;
break;
}
case 2:
{
ho = new HX;
break;
}
case 3:
{
ho = new LB;
break;
}
default:
break;
}
if (ho != NULL)
{
ho->skill1();
ho->skill2();
ho->uskill();
//这里想要调用西施的show函数,基类指针指向西施对象,注意show()不是虚函数
//ho->show();//错误
//if (select == 1)
//{
//XS* pxs = (XS*)ho;//c语言强制转换风格,程序员必须要保证目标类型正确
//pxs->show();//添加判断之后,这段代码是安全的
//}
//XS* pxs = (XS*)ho;//不添加英雄的判断,直接进行强制类型转换,指针类型与子类对象类型不匹配
//pxs->show();//假如基类指针没有指向西施,但是此处经过强转,仍然回执行show函数,代码不安全,执行了其他子类的非虚函数
XS* pxs = dynamic_cast<XS*>(ho);//把基类指针转换为派生类指针
if (pxs != NULL)
{
Hero* ptr = pxs;//派生类指针可以直接赋值给基类指针,不需要任何转换
pxs->show();
}
}
test01();
delete ho;
system("pause");
return 0;
}
<3>reinterpret_cast
static_cast不能用于类型不同的指针(引用)之间的转换,(不考虑有继承关系的情况),reinterpret_cast可以。
reinterpret_cast意思是重新解释,能够将一种对象类型转换成另外一种不管它们是否有关系。
语法:reinterpret<目标类型>(表达式)
<目标类型>和(表达式)中必须有一个是指针(引用)类型。
reinterpret_cast不能丢掉(表达式)的const和volitale属性。
应用场景:
(1)改变指针或者引用类型。
(2)将指针(引用)转换成整型变量,整形与指针占用的字节数必须一致,否则可能损失精度。
(3)把一个整形变量转换成指针(引用)。
reinterpret_cast()测试代码
#include<iostream>
using namespace std;
//1 用于内置数据类型之间的转换
void test01()
{
int ii = 10;
long ll = ii; //绝对安全,可以隐式转换,不会出现警告
double dd = 1.23;
long ll1 = dd;//可以隐式转换,但是,会出现可能丢失数据的警告。
long ll2 = (long)dd;//C风格: 显示转换,不会出现警告
//long ll3 = reinterpret_cast<long>(dd);//reinterprert_cast不能用于内置数据类型转换
//cout << ii << " " << ll << " " << dd << " " << ll1 << " " << ll2 << " " << ll3 << endl;
}
void test02()
{
int ii = 10;
//double* pd1 = ⅈ //错误,不能隐式类型转换
double* pd2 = (double*)ⅈ //正确 C风格,类型强转
double* pd3 = reinterpret_cast<double*>(&ii);//正确 reinterpret_cast可以用于指针和引用之间的直接转换
void* pv = ⅈ //任何类型的指针都可以隐式转换成void*
double* pd4 = static_cast<double*>(pv);//static_cast可以把void*类型指针转换为其他类型指针
}
void func(void* ptr)
{
int ii = reinterpret_cast<int>(ptr);
cout << "ii= " << ii << endl;
}
int main()
{
int ii = 10;
func(reinterpret_cast<void*>(ii));
system("pause");
return 0;
}
<4>const_cast
static_cast 不能丢掉指针(引用)的cosnt和volitale属性,const_cast可以。
const_cast专门用于去除和增加const属性。
const_cast测试代码:
#include<iostream>
using namespace std;
void func(int* ptr)
{
}
int main()
{
//const int aa = 10;
//int bb = aa;//对于普通变量来说,不存在去除const的说法。
const int* aa = nullptr;
//int* bb = aa;//对于指针不能隐式丢掉const
int* bb = (int*)aa;
int* cc = const_cast<int*>(aa);
//func(aa);//不去const传参不符
func(const_cast<int*>(aa));
system("pause");
return 0;
}