在C++中主要分为四种cast,分别是:static_cast、dynamic_cast、const_cast和reinterpret_cast
dynamic_cast动态类型转换
首先,我们明确基类和派生类之间的两个转换术语
向上转换(upcast):派生类向基类转换
向下站还(downcast):基类向派生类转换
下面就上面两种类型转换,说明一下dynamic_cast在其中的情况
dynamic_cast转换的base类至少带有一个虚函数
对于向上类型转换的时候,总是成功并且安全的
对于向下类型转换不都是成功的。对于指针,如果转换不成功,返回空指针。对于引用,如果转换不成功,则会跑出异常(std::bad_cast)
1.向上转换是正确的
class Base
{
public:
Base(){};
virtual void Show(){cout<<"This is Base calss";}
};
class Derived:public Base
{
public:
Derived(){};
void Show(){cout<<"This is Derived class";}
};
int main()
{
Base *base ;
Derived *der = new Derived;
//base = dynamic_cast<Base*>(der); //正确,但不必要。
base = der; //先上转换总是安全的
base->Show();
system("pause");
}
2.dynamic_cast与继承层次的指针
有两种情况:
第一种基类指针所指的对象是派生类类型,第二种是基类指针所指对象是基类类型,dynamic_cast在运行是检查转换失败返回0
#include "stdafx.h"
#include<iostream>
using namespace std;
class Base
{
public:
Base(){};
virtual void Show(){cout<<"This is Base calss";}
};
class Derived:public Base
{
public:
Derived(){};
void Show(){cout<<"This is Derived class";}
};
int main()
{
//这是第一种情况
Base* base = new Derived;
if(Derived *der= dynamic_cast<Derived*>(base))
{
cout<<"第一种情况转换成功"<<endl;
der->Show();
cout<<endl;
}
//这是第二种情况
Base * base1 = new Base;
if(Derived *der1 = dynamic_cast<Derived*>(base1))
{
cout<<"第二种情况转换成功"<<endl;
der1->Show();
}
else
{
cout<<"第二种情况转换失败"<<endl;
}
delete(base);//new与delete要配对使用
delete(base1);
system("pause");
}
运行结果:
3.dynamic_cast和引用类型
Derived c;
Derived & der2= c;
Base & base2= dynamic_cast<Base&>(der2);//向上转换,安全
base2.Show();
与指针不同的是,并不存在空引用,所以引用的dynamic_cast检测失败时会抛出一个bad_cast异常
int main()
{
//第一种情况,转换成功
Derived b ;
Base &base1= b;
Derived &der1 = dynamic_cast<Derived&>(base1);
cout<<"第一种情况:";
der1.Show();
cout<<endl;
//第二种情况
Base a ;
Base &base = a ;
cout<<"第二种情况:";
try{
Derived & der = dynamic_cast<Derived&>(base);
}
catch(bad_cast)
{
cout<<"转化失败,抛出bad_cast异常"<<endl;
}
system("pause");
}
运行结果:
4.static_cast静态类型转换
效果和C风格的强制类型转换是一样的
double a = 1.999;
void * vptr = & a;
double * dptr = static_cast<double*>(vptr);
cout<<*dptr<<endl;//输出1.999
static_cast也可以用在于基类与派生类指针或引用类型之间的转换。然而它不做运行时的检查,不如dynamic_cast安全。。static_cast仅仅是依靠类型转换语句中提供的信息来进行转换,而dynamic_cast则会遍历整个类继承体系进行类型检查,因此dynamic_cast在执行效率上比static_cast要差一些。
class ANIMAL
{
public:
ANIMAL():_type("ANIMAL"){};
virtual void OutPutname(){cout<<"ANIMAL";};
private:
string _type ;
};
class DOG:public ANIMAL
{
public:
DOG():_name("大黄"),_type("DOG"){};
void OutPutname(){cout<<_name;};
void OutPuttype(){cout<<_type;};
private:
string _name ;
string _type ;
};
int main()
{
//基类指针转为派生类指针,且该基类指针指向基类对象。
ANIMAL * ani1 = new ANIMAL ;
DOG * dog1 = static_cast<DOG*>(ani1);
//dog1->OutPuttype();//错误,在ANIMAL类型指针不能调用方法OutPutType();在运行时出现错误。
//基类指针转为派生类指针,且该基类指针指向派生类对象,
ANIMAL * ani3 = new DOG;
DOG* dog3 = static_cast<DOG*>(ani3);
dog3->OutPutname(); //正确
//子类指针转为父类指针
DOG *dog2= new DOG;
ANIMAL *ani2 = static_cast<DOG*>(dog2);
ani2->OutPutname(); //正确,结果输出为大黄
//
system("pause");
}
5.const_cast去除const属性
6.reinterpret_cast 运算符并不会改变括号中运算对象的值,而是对该对象从位模式上进行重新解释,指针类型会教导编译器如何解释某个特定地址中的内存内容及其大小
#include <iostream>
using namespace std;
int main(int argc, char** argv)
{
int num = 0x00636261;//用16进制表示32位int,0x61是字符'a'的ASCII码
int * pnum = #
char * pstr = reinterpret_cast<char *>(pnum);
cout<<"pnum指针的值: "<<pnum<<endl;
cout<<"pstr指针的值: "<<static_cast<void *>(pstr)<<endl;//直接输出pstr会输出其指向的字符串,这里的类型转换是为了保证输出pstr的值
cout<<"pnum指向的内容: "<<hex<<*pnum<<endl;
cout<<"pstr指向的内容: "<<pstr<<endl;
return 0;
}
使用reinterpret_cast运算符把pnum从int转变成char类型并用于初始化pstr后,pstr也指向num的内存区域,但是由于pstr是char类型的,通过pstr读写num内存区域将不再按照整型变量的规则,而是按照char型变量规则。一个char型变量占用一个Byte,对pstr解引用得到的将是一个字符,也就是’a’。而在使用输出流输出pstr时,将输出pstr指向的内存区域的字符,那pstr指向的是一个的字符,那为什么输出三个字符呢?这是由于在输出char指针时,输出流会把它当做输出一个字符串来处理,直至遇到’\0’才表示字符串结束。对代码稍做改动,就会得到不一样的输出结果,例如将num的值改为0x63006261,输出的字符串就变为”ab”。