深入解析C++中的动态类型转换与静态类型转换运算符

深入解析C++中的动态类型转换与静态类型转换运算符,第1张

概述dynamic_cast运算符将 *** 作数expression转换类型为type-id的对象。语法dynamic_cast<type-id>(expression)

dynamic_cast 运算符
将 *** 作数 Expression 转换成类型为type-ID 的对象。
语法

dynamic_cast < type-ID > ( Expression )

备注
type-ID 必须是一个指针或引用到以前已定义的类类型的引用或“指向 voID 的指针”。如果 type-ID 是指针,则Expression 的类型必须是指针,如果 type-ID 是引用,则为左值。
有关静态和动态强制转换之间区别的描述,以及各在什么情况下适合使用,请参见 static_cast。
在托管代码中的 dynamic_cast的行为中有两个重大更改。
为指针的dynamic_cast 对指向装箱的枚举的基础类型的指针将在运行时失败,则返回 0 而不是已转换的指针。
dynamic_cast 将不再引发一个异常,当 type-ID 是指向值类型的内部指针,则转换在运行时失败。该转换将返回 0 指示运行值而不是引发。
如果 type-ID 是指向 Expression的明确的可访问的直接或间接基类的指针,则结果是指向 type-ID 类型的唯一子对象的指针。例如:

// dynamic_cast_1.cpp// compile with: /cclass B { };class C : public B { };class D : public C { };voID f(D* pd) {  C* pc = dynamic_cast<C*>(pd);  // ok: C is a direct base class                  // pc points to C subobject of pd   B* pb = dynamic_cast<B*>(pd);  // ok: B is an indirect base class                  // pb points to B subobject of pd}

此转换类型称为“向上转换”,因为它将在类层次结构上的指针,从派生的类移到该类派生的类。向上转换是一种隐式转换。
如果 type-ID 为 voID*,则做运行时进行检查确定 Expression的实际类型。结果是指向 by Expression 的完整的对象的指针。例如:

// dynamic_cast_2.cpp// compile with: /c /GRclass A {virtual voID f();};class B {virtual voID f();};voID f() {  A* pa = new A;  B* pb = new B;  voID* pv = dynamic_cast<voID*>(pa);  // pv Now points to an object of type A  pv = dynamic_cast<voID*>(pb);  // pv Now points to an object of type B}

如果 type-ID 不是 voID*,则做运行时进行检查以确定是否由 Expression 指向的对象可以转换为由 type-ID指向的类型。
如果 Expression 类型是 type-ID类型的基类,则做运行时检查来看是否 Expression 确实指向 type-ID类型的完整对象。如果为 true,则结果是指向 type-ID类型的完整对象的指针。例如:

// dynamic_cast_3.cpp// compile with: /c /GRclass B {virtual voID f();};class D : public B {virtual voID f();};voID f() {  B* pb = new D;  // unclear but ok  B* pb2 = new B;  D* pd = dynamic_cast<D*>(pb);  // ok: pb actually points to a D  D* pd2 = dynamic_cast<D*>(pb2);  // pb2 points to a B not a D}

此转换类型称为“向下转换”,因为它将在类层次结构下的指针,从给定的类移到该类派生的类。
对于多重继承,引入多义性的可能性。考虑下图中显示的类层次结构。
对于 CLR 类型,如果转换可以隐式执行,则 dynamic_cast 结果为 no-op,如果转换失败,则 MSIL isinst 指令将执行动态检查并返回 nullptr。
以下示例使用 dynamic_cast 以确定一个类是否为特殊类型的实例:

// dynamic_cast_clr.cpp// compile with: /clrusing namespace System;voID PrintObjectType( Object^o ) {  if( dynamic_cast<String^>(o) )   Console::Writeline("Object is a String");  else if( dynamic_cast<int^>(o) )   Console::Writeline("Object is an int");}int main() {  Object^o1 = "hello";  Object^o2 = 10;  PrintObjectType(o1);  PrintObjectType(o2);}

显示多重继承的类层次结构
显示多继承的类层次结构
指向类型 D 对象的指针可以安全地强制转换为 B 或 C。但是,如果 D 强制转换为指向 A 对象的指针,会导致 A 的哪个实例?这将导致不明确的强制转换错误。若要避免此问题,可以执行两个明确的转换。例如:

// dynamic_cast_4.cpp// compile with: /c /GRclass A {virtual voID f();};class B {virtual voID f();};class D : public B {virtual voID f();};voID f() {  D* pd = new D;  B* pb = dynamic_cast<B*>(pd);  // first cast to B  A* pa2 = dynamic_cast<A*>(pb);  // ok: unambiguous}

当使用虚拟基类时,其他多义性问题会被引入。考虑下图中显示的类层次结构。

显示虚拟基类的类层次结构
在此层次结构中,A 是虚拟基类。对于虚拟基类的定义。给定一个 E 类实例和一个指向 A 子对象的指针,指向 B 指针的 dynamic_cast 将失败于多义性。必须先将强制转换回完整 E 对象,然后以明确的方式反向沿层次结构,到达正确的 B 对象。
考虑下图中显示的类层次结构。

给定一个 E 类型的对象和一个指向 D 子对象的指针,从 D 子对象定位到最左侧的 A 子对象,可进行三个转换。可以从 D 指针到 E 指针执行 dynamic_cast 转换,然后从 E 到 B 执行转换(dynamic_cast 或隐式转换),最后从 B 到 A 执行隐式转换。例如:

// dynamic_cast_5.cpp// compile with: /c /GRclass A {virtual voID f();};class B : public A {virtual voID f();};class C : public A { };class D {virtual voID f();};class E : public B,public C,public D {virtual voID f();};voID f(D* pd) {  E* pe = dynamic_cast<E*>(pd);  B* pb = pe;  // upcast,implicit conversion  A* pa = pb;  // upcast,implicit conversion}

dynamic_cast 运算符还可以使用执行 “相互转换”。使用同一个类层次结构可能进行指针转换,例如: 从B 子对象转换到D子对象(只要整个对象是类转换型E。
考虑相互转换,实际上从指针转换到 D 到指针到最左侧的 A 子对象只要两个步骤。可以从 D 到 B 执行相互转换,然后从 B 到 A 执行隐式转换。例如:

// dynamic_cast_6.cpp// compile with: /c /GRclass A {virtual voID f();};class B : public A {virtual voID f();};class C : public A { };class D {virtual voID f();};class E : public B,public D {virtual voID f();};voID f(D* pd) {  B* pb = dynamic_cast<B*>(pd);  // cross cast  A* pa = pb;  // upcast,implicit conversion}

通过 dynamic_cast 将 null 指针值转换到目标类型的 null 指针值。
当您使用 dynamic_cast < type-ID > ( Expression )时,如果Expression无法安全地转换成类型 type-ID,则运行时检查会引起变换失败。例如:

// dynamic_cast_7.cpp// compile with: /c /GRclass A {virtual voID f();};class B {virtual voID f();};voID f() {  A* pa = new A;  B* pb = dynamic_cast<B*>(pa);  // fails at runtime,not safe;  // B not derived from A}

指针类型的非限定转换的值是 null 指针。引用类型的非限定转换会引发 bad_cast 异常。   如果 Expression 不指向也不引用有效的对象,则__non_rtti_object 异常引发。
有关异常 __non_rtti_object 的解释,请参见 typeID。
以下示例创建基类(结构 A)指针,为一个对象(结构 C)。这以及在该情况是虚函数,启用运行时多态性。
该示例也在层次结构中调用非虚函数。

// dynamic_cast_8.cpp// compile with: /GR /EHsc#include <stdio.h>#include <iostream>struct A {  virtual voID test() {    printf_s("in A\n");  }};struct B : A {  virtual voID test() {    printf_s("in B\n");  }  voID test2() {    printf_s("test2 in B\n");  }};struct C : B {  virtual voID test() {    printf_s("in C\n");  }  voID test2() {    printf_s("test2 in C\n");  }};voID Globaltest(A& a) {  try {    C &c = dynamic_cast<C&>(a);    printf_s("in GlobalTest\n");  }  catch(std::bad_cast) {    printf_s("Can't cast to C\n");  }}int main() {  A *pa = new C;  A *pa2 = new B;  pa->test();  B * pb = dynamic_cast<B *>(pa);  if (pb)    pb->test2();  C * pc = dynamic_cast<C *>(pa2);  if (pc)    pc->test2();  C ConStack;  Globaltest(ConStack);  // will fail because B kNows nothing about C  B BonStack;  Globaltest(BonStack);}

输出:

in Ctest2 in Bin GlobalTest

static_cast 运算符
仅根据表达式中存在的类型,将 Expression 转换为 type-ID,类型。
语法

static_cast <type-ID> ( Expression ) 

备注
在标准 C++ 中,不进行运行时类型检查来帮助确保转换的安全。在 C++/CX 中,将执行编译时和运行时检查。
static_cast 运算符可用于将指向基类的指针转换为指向派生类的指针等 *** 作。此类转换并非始终安全。
通常使用 static_cast 转换数值数据类型,例如将枚举型转换为整型或将整型转换为浮点型,而且你能确定参与转换的数据类型。 static_cast 转换安全性不如 dynamic_cast 转换,因为 static_cast 不执行运行时类型检查,而 dynamic_cast 执行该检查。对不明确的指针的 dynamic_cast 将失败,而 static_cast 的返回结果看似没有问题,这是危险的。尽管 dynamic_cast 转换更加安全,但是 dynamic_cast 只适用于指针或引用,而且运行时类型检查也是一项开销。
在下面的示例中,因为 D 可能有不在 B 内的字段和方法,所以行 D* pd2 = static_cast<D*>(pb); 不安全。但是,因为 D 始终包含所有 B,所以行 B* pb2 = static_cast<B*>(pd); 是安全的转换。

// static_cast_Operator.cpp// compile with: /LDclass B {};class D : public B {};voID f(B* pb,D* pd) {  D* pd2 = static_cast<D*>(pb);  // Not safe,D can have fIElds                  // and methods that are not in B.  B* pb2 = static_cast<B*>(pd);  // Safe conversion,D always                  // contains all of B.}

与 dynamic_cast 不同,pb 的 static_cast 转换不执行运行时检查。由 pb 指向的对象可能不是 D 类型的对象,在这种情况下使用 *pd2 会是灾难性的。例如,调用 D 类(而非 B 类)的成员函数可能会导致访问冲突。
dynamic_cast 和 static_cast 运算符可以在整个类层次结构中移动指针。然而,static_cast 完全依赖于转换语句提供的信息,因此可能不安全。例如:

// static_cast_Operator_2.cpp// compile with: /LD /GRclass B {public:  virtual voID test(){}};class D : public B {};voID f(B* pb) {  D* pd1 = dynamic_cast<D*>(pb);  D* pd2 = static_cast<D*>(pb);}

如果 pb 确实指向 D 类型的对象,则 pd1 和 pd2 将获取相同的值。如果 pb == 0,它们也将获取相同的值。
如果 pb 指向 B 类型的对象,而非指向完整的 D 类,则 dynamic_cast 足以判断返回零。但是,static_cast 依赖于程序员的断言,即 pb 指向 D 类型的对象,因而只是返回指向那个假定的 D 对象的指针。
因此,static_cast 可以反向执行隐式转换,而在这种情况下结果是不确定的。这需要程序员来验证 static_cast 转换的结果是否安全。
该行为也适用于类以外的类型。例如,static_cast 可用于将 int 转换为 char。但是,得到的 char 可能没有足够的位来保存整个 int 值。同样,这需要程序员来验证 static_cast 转换的结果是否安全。
static_cast 运算符还可用于执行任何隐式转换,包括标准转换和用户定义的转换。例如:

// static_cast_Operator_3.cpp// compile with: /LD /GRtypedef unsigned char BYTE;voID f() {  char ch;  int i = 65;  float f = 2.5;  double dbl;  ch = static_cast<char>(i);  // int to char  dbl = static_cast<double>(f);  // float to double  i = static_cast<BYTE>(ch);}

static_cast 运算符可以将整数值显式转换为枚举类型。如果整型值不在枚举值的范围内,生成的枚举值是不确定的。
static_cast 运算符将 null 指针值转换为目标类型的 null 指针值。
任何表达式都可以通过 static_cast 运算符显式转换为 voID 类型。目标 voID 类型可以选择性地包含 const、volatile 或 __unaligned 特性。
static_cast 运算符无法转换掉 const、volatile 或 __unaligned 特性。

总结

以上是内存溢出为你收集整理的深入解析C++中的动态类型转换与静态类型转换运算符全部内容,希望文章能够帮你解决深入解析C++中的动态类型转换与静态类型转换运算符所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1249797.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-07
下一篇 2022-06-07

发表评论

登录后才能评论

评论列表(0条)

保存