C++基础入门篇之强制转换
引言
假设有基类A,包含了虚函数func1,以及有派生类B,继承于类A,派生类B中实现了函数func1。此时可以用A类型的指针指向B类型的对象,并用A类型的指针调用B类型对象中的函数func1。这时,就形成了多态。包含虚函数的类A,我们也称为多态类。
由于派生类B完整包含了基类A的所有定义,将B类型的指针转换为A类型的指针总是安全的。
而将A类型的指针强制转换为B类型的指针时,如果A类型指针指向的对象确实为B类型的对象,那么转换也是安全的。此时,该B类型对象被称为完整对象(completeobject)。
强制转换有哪些类型?
C++包含了以下几种强制转换运算符,这些运算符用于消除老式C语言转换中的存在的歧义和隐患:
- dynamic_cast
- static_cast
- const_cast
- reinterpret_cast
- safe_cast
本文会着重介绍如何使用dynamic_cast和static_cast。
提醒:
除非必须,不要使用const_cast和reinterpret_cast,因为它们存在一些老式C语言转换中的隐患。
dynamic_cast运算符
语法:
dynamic_cast(expression)
type-id必须是一个指针或者引用,指向/引用已定义的类类型或者void。如果type-id是指针,则expression必须也为指针类型,如果type-id是引用,expression必须为左值类型。
如果type-id是void*,那么在运行时将检测expression的实际类型。其结果返回expression指向的完整对象。
如非需要,现代C++中应该避免使用void指针,因为容易出错。
下面看些示例,了解dynamic_cast的使用方式。
示例1:
classRoot{}; classBase:publicRoot{}; classDerived:publicBase{}; voidf(Derived*pd){ Base*pb=dynamic_cast(pd);//ok:Baseisadirectbaseclass //pbpointstoBasesubobjectofpd Root*pr=dynamic_cast (pd);//ok:Rootisanindirectbaseclass //prpointstoRootsubobjectofpd }
示例1中提到了子对象(subobject)的概念,注意与子类型进行区分:
- Root类型包含子类型Base,Base类型包含子类型Derived。
- Derived对象包含了Base类型的子对象,Base类型的子对象又包含了Root类型的子对象。
再联系下前面说的:派生类完整包含了基类的所有定义。
示例2:
classB{virtualvoidf();}; classD:publicB{virtualvoidf();}; voidf(){ B*pb=newD;//unclearbutok B*pb2=newB; D*pd=dynamic_cast(pb);//ok:pbactuallypointstoaD D*pd2=dynamic_cast (pb2);//pb2wasnullptr. }
示例2中通过dynamic_cast转换为pd2时,不会报错,返回nullptr。但如果同样地情况转换的对象是引用类型,那么运行时会抛出std::bad_cast异常。如果pb2指向/引用的对象无效,同样也会抛出异常。
示例3:
#include#include structA{ virtualvoidtest(){ printf_s("inA\n"); } }; structB:A{ virtualvoidtest(){ printf_s("inB\n"); } voidtest2(){ printf_s("test2inB\n"); } }; structC:B{ virtualvoidtest(){ printf_s("inC\n"); } voidtest2(){ printf_s("test2inC\n"); } }; voidGlobaltest(A&a){ try{ C&c=dynamic_cast (a); printf_s("inGlobalTest\n"); } catch(std::bad_cast){ printf_s("Can'tcasttoC\n"); } } intmain(){ A*pa=newC; A*pa2=newB; pa->test(); B*pb=dynamic_cast(pa); if(pb) pb->test2(); C*pc=dynamic_cast (pa2); if(pc) pc->test2(); CConStack; Globaltest(ConStack); //willfailbecauseBknowsnothingaboutC BBonStack; Globaltest(BonStack); } Output: inC test2inB inGlobalTest Can'tcasttoC
static_cast运算符
语法:
static_cast(expression)
static_cast通常用于数值类型转换,例如枚举和整型,整型和浮点类型的转换。
在标准C++中,static_cast转换没有运行时检测来保证安全性。在C++/CX中,则包含了编译和运行时检测。
static_cast运算符能够用于将基类指针转换为派生类指针,但这样的转换不总是安全的。
下面还是通过示例进行讲解。
示例1:
classB{}; classD:publicB{}; voidf(B*pb,D*pd){ D*pd2=static_cast(pb);//Notsafe,Dcanhavefields //andmethodsthatarenotinB. B*pb2=static_cast(pd);//Safeconversion,Dalways //containsallofB. }
示例1中pd2不为空,当用指针pd2调用B类型对象不存在的方法或者成员时可能会发生运行时错误(比如调用虚函数)或者返回非预期的值。
示例2:
typedefunsignedcharBYTE; voidf(){ charch; inti=65; floatf=2.5; doubledbl; ch=static_cast(i);//inttochar dbl=static_cast (f);//floattodouble i=static_cast (ch); }
示例2中static_cast运算符显示地将内置类型进行转换。
关于static_cast运算符,还有以下几种使用情况:
- static_cast能够显式的将整型转换为枚举类型。如果整型值不在枚举值范围内,那么返回的枚举值是未定义的。
- static_cast能将任何expression显式地转换为void类型。
- static_cast操作符不会去除const,volatile,__unaligned属性。
区分几种强制转换的使用场景
dynamic_cast主要用于多态类型的强制转换,而static_cast主要用于非多态类型的强制转换。
static_cast转换不像dynamic_cast那样安全。因为static_cast没有运行时检测。通过dynamic_cast进行转换时,一旦存在歧义,就会导致失败,然而static_cast会像没有错误发生一样返回结果。尽管dynamic_cast更加安全,但dynamic_cast仅适用于指针和引用,并且运行时检测是需要消耗性能的。
示例:
classB{ public: virtualvoidTest(){} }; classD:publicB{}; voidf(B*pb){ D*pd1=dynamic_cast(pb); D*pd2=static_cast (pb); }
如果pb实际指向类型D或者pd==0,那么pd1和pd2将获得相同的值。
如果pb实际指向类型B,那么dynamic_cast会返回0。但是static_cast依赖于expression认定pb指向D类型对象,于是简单的返回D类型的指针。
结果就是,static_cast转换会继续执行,但其返回结果是未定义的。这就需要调用者去进一步验证转换结果是有效的。
引用
https://docs.microsoft.com/en-us/cpp/cpp/casting?view=msvc-160
总结
到此这篇关于C++基础入门篇之强制转换的文章就介绍到这了,更多相关C++强制转换内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。