c/c++语言开发共享C++基础入门篇之强制转换

引言假设有基类 a,包含了虚函数 func1,以及有派生类 b,继承于类 a,派生类 b 中实现了函数 func1。此时可以用 a 类型的指针指向 b 类型的对象,并用 a 类型的指针调用 b 类型对


引言

假设有基类 a,包含了虚函数 func1,以及有派生类 b,继承于类 a,派生类 b 中实现了函数 func1。此时可以用 a 类型的指针指向 b 类型的对象,并用 a 类型的指针调用 b 类型对象中的函数 func1。这时,就形成了多态。包含虚函数的类 a,我们也称为多态类。

由于派生类 b 完整包含了 基类 a 的所有定义,将 b 类型的指针转换为 a 类型的指针总是安全的。

而将 a 类型的指针强制转换为 b 类型的指针时,如果 a 类型指针指向的对象确实为 b 类型的对象,那么转换也是安全的。此时,该 b 类型对象被称为完整对象(complete object)。

强制转换有哪些类型?

c++ 包含了以下几种强制转换运算符,这些运算符用于消除老式 c 语言转换中的存在的歧义和隐患:

  • dynamic_cast
  • static_cast
  • const_cast
  • reinterpret_cast
  • safe_cast

c/c++开发分享C++基础入门篇之强制转换会着重介绍如何使用 dynamic_cast 和 static_cast。

提醒:

除非必须,不要使用 const_cast 和 reinterpret_cast,因为它们存在一些老式 c 语言转换中的隐患。

dynamic_cast 运算符

语法:

   dynamic_cast <type-id> (expression)  

type-id 必须是一个指针或者引用,指向/引用已定义的类类型或者 void。如果type-id 是指针,则 expression 必须也为指针类型,如果 type-id 是引用,expression 必须为左值类型。

如果 type-id 是 void*,那么在运行时将检测 expression 的实际类型。其结果返回 expression 指向的完整对象。

如非需要,现代 c++ 中应该避免使用 void 指针,因为容易出错。

下面看些示例,了解 dynamic_cast 的使用方式。

示例1:

  class root { };  class base : public root { };  class derived : public base { };    void f(derived* pd) {   base* pb = dynamic_cast<base*>(pd); // ok: base is a direct base class           // pb points to base subobject of pd   root* pr = dynamic_cast<root*>(pd); // ok: root is an indirect base class           // pr points to root subobject of pd  }    

示例1 中提到了子对象(subobject)的概念,注意与子类型进行区分:

  • root 类型包含子类型 base,base 类型包含子类型 derived。
  • derived 对象包含了 base 类型的子对象,base 类型的子对象又包含了 root 类型的子对象。

再联系下前面说的:派生类完整包含了基类的所有定义。

示例2:

  class 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 was nullptr.  }    

示例2 中 通过 dynamic_cast 转换为 pd2 时,不会报错,返回 nullptr。但如果同样地情况转换的对象是引用类型,那么运行时会抛出 std::bad_cast 异常。如果 pb2 指向/引用的对象无效,同样也会抛出异常。

示例3:

  #include <stdio.h>  #include <iostream>    struct a {   virtual void test() {    printf_s("in an");   }  };    struct b : a {   virtual void test() {    printf_s("in bn");   }     void test2() {    printf_s("test2 in bn");   }  };    struct c : b {   virtual void test() {    printf_s("in cn");   }     void test2() {    printf_s("test2 in cn");   }  };    void globaltest(a& a) {   try {    c &c = dynamic_cast<c&>(a);    printf_s("in globaltestn");   }   catch(std::bad_cast) {    printf_s("can't cast to cn");   }  }    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);  }  output:    in c  test2 in b  in globaltest  can't cast to c    

static_cast 运算符

语法:

  static_cast <type-id> (expression)  

static_cast 通常用于数值类型转换,例如枚举和整型,整型和浮点类型的转换。

在标准 c++ 中,static_cast 转换没有运行时检测来保证安全性。在 c++/cx 中,则包含了编译和运行时检测。

static_cast 运算符能够用于将基类指针转换为派生类指针,但这样的转换不总是安全的。

下面还是通过示例进行讲解。

示例1:

  class 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.  }    

示例1 中 pd2 不为空,当用指针 pd2 调用 b 类型对象不存在的方法或者成员时可能会发生运行时错误(比如调用虚函数)或者返回非预期的值。

示例2:

  typedef 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);  }    

示例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 仅适用于指针和引用,并且运行时检测是需要消耗性能的。

示例:

  class 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 或者 pd == 0,那么 pd1 和 pd2 将获得相同的值。

如果 pb 实际指向类型 b,那么 dynamic_cast 会返回 0。但是 static_cast 依赖于 expression 认定 pb 指向 d 类型对象,于是简单的返回 d 类型的指针。

结果就是,static_cast 转换会继续执行,但其返回结果是未定义的。这就需要调用者去进一步验证转换结果是有效的。

引用

总结

到此这篇关于c++基础入门篇之强制转换的文章就介绍到这了,更多相关c++强制转换内容请搜索<计算机技术网(www.ctvol.com)!!>以前的文章或继续浏览下面的相关文章希望大家以后多多支持<计算机技术网(www.ctvol.com)!!>!

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/c-cdevelopment/595977.html

(0)
上一篇 2021年5月8日
下一篇 2021年5月8日

精彩推荐