静态强制数据转换
-
c++在原有C语言的强制数据转换的基础之上更新了强制数据转换的方式
-
C语言的强制数据转换的方式:type a = (type)data; 或 type a = type(data);其中的数据前的(type)data或type(data)就是说明无论data为何种类型,都将其强制数据转换为type类型,后者与Python方式相一致
int a = (int)1.234; int a = int(1.234); //(二者效果一样)
-
c++新增强制数据转换方式:
type a = static_cast<type>(data);
其中的static_cast<type>(data)表示无论data为何种数据类型,都将其强制数据转换为type
int a = static_cast<int>(1.234);
动态强制数据转换
-
不同于静态强制数据转换,动态强制数据转换主要用于基类与派生类之间的转换
-
声明格式:
type a = dynamic_cast<type>(data);
基于其用途,其更准确的声明格式应为:
class_type* pa = dynamic_cast<class_type*>(pb) [ 0 ] ^{[0]} [0]
-
意义:使用基类的指针或引用去指向派生类的对象时很常见的方式,实际上在这样的操作中编译器会隐式的执行向上的数据转换(即由派生类向基类进行转换),而实际需求中有时还需要进行向下的强制类型转换(即由基类向派生类进行转换),但由于这种转换的结果往往是不确定的,所以需要显式声明,如:
class base B{ public: int a; }; //定义基类B class derive D : public B{ public : int b; }; //定义派生类D void show_member(B* obj){ cout << obj->a << endl; } //定义全局函数
在上述代码中,通过函数show_member只能去显示基类中的成员的值,因为形参为基类的指针,无论实参是基类还是派生类,都将隐式的向上转化为基类指针,也就只能访问基类的成员部分,如果希望当传入派生类指针的时候将派生部分的成员也显示出来,就需要在函数中判断并显式的向下转化回派生类的指针,即将show_member定义为:
void show_member(B* obj){ cout << obj->a << endl; auto* p = dynamic_cast<D*>(obj); //强制向下转换为派生类指针后赋值给p if(p != nullptr){ //因为转换失败会返回空指针,可作判断条件 cout << p->b << endl; //转换成功则输出派生部分成员 } }
-
dynamic_cast什么时候会转换失败呢?-----基于上述例子,对以下几种情况进行分析:
//首先声明: class D d, *pd = nullptr; class B b, *pb = nullptr; pb = &b 或 pd = &d; //正确,基类指针指向基类对象或派生类指针指向派生类对象 pb = &d; //正确,基类指针指向派生类成员 pb = dynamic_cast<D*>(&d); //正确,是上式的显式声明 pd = &b; //错误,向下转换必须显示声明,且基类空间无法扩充为派生类空间 pd = dynamic_cast<B*>(&b); //运行正确但转化失败,pd将得到空指针,因为基类空间无法扩充为派生类空间 B* temp = &d; pd = temp; //错误,向下转换需要显式声明 B* temp = &d; pd = dynamic_cast<D*>(temp); //正确,temp为基类指针,但所指空间为派生类空间通过显式动态转换将其还原
综上,可以得出结论,当强制动态类型转换是向上转换时(派生类向基类转换),一定会成功且无需显式声明,当强制动态转换用于向下转换时(基类向派生类),只有当所转换的指针或引用所指空间本身就是派生类空间时能够成功,否则将转换失败(即可以理解为,动态类型转换可以将派生类空间截断作为基类空间使用,但无法无中生有将基类空间扩充作为派生类空间)故此,强制动态转换实际上是用于对向上转换后的向下还原,因为无论使用基类指针或引用去指向派生类对象的时候,虽然通过隐式转换将其所指空间截断,但其空间本质上依然是派生类空间,故可无需进行扩充,可直接转换,另一种情况是,当所转换的指针所指向的空间是另一个继承基类的派生类空间时,也将无法转换而返回空指针
注[0]:dynamic_cast用于指针转换时,失败返回空指针,用于引用的转换时,失败将抛出异常
注:除上述两种转换外还有reinterpret_cast和const_cast
初始化
-
c++11标准以前完全继承C语言初始化方式,c++11新增了列表初始化方式,分为直接列表初始化和拷贝列表初始化,间接方式C语言也有存在,主要新增直接列表初始化方式
-
列表初始化方式限制进行隐式的数据类型转换,如 int x = 1.234;正确,编译器自动进行隐式的数据类型转换,精度丢失,int x{1.234};或int x{1.234};会报错,可以显式强制类型转换
//before c++11: double x = 1.234; double x(1.234); double x[] = {1.2, 3.4}; char x[] = {"1234"}; //after c++11: //直接列表初始化: double x{}; // x == 0 double x{1.234}; double x[]{1.2, 3.4}; char x[]{"12334"}; //间接列表初始化: double x = {1.234}; double x[] = {1.2, 3.4}; char x[] = {"12334"}; char x[] = {}