Skip to content

Files

Latest commit

c70125a · Jan 8, 2022

History

History
57 lines (32 loc) · 5.37 KB

08.md

File metadata and controls

57 lines (32 loc) · 5.37 KB

八、C++ 中的强制转换

概述

有五种不同的方式铸造变量。它们之间有重叠,尤其是 C 风格的演员阵容和所有其他演员阵容,但各有各的用途。把它们都学会是很好的,所以你可以根据你的特殊需要使用最好的演员阵容,而不是使用任何碰巧有效的演员阵容。如果你需要快速参考,我推荐 StackOverflow 上的这篇帖子。

我在这里不讨论隐式转换,原因很简单,这是一个基本概念,有几乎无限多的变体。如果我写float f = 10;,我已经隐式地将一个整数转换为一个浮点数,并将其结果存储在f中。您也可以使用 address-of 运算符将 B 类型的对象隐式转换为指向其基类 A 的指针,或者通过执行正常赋值来转换为对其基类 A 的引用。

const_cast

const_cast操作员可以添加和删除constvolatile。使用它来添加这些属性中的任何一个都可以。你很少这么做,但如果你做了,你就能做到。

它移除const的能力是你永远不应该在 C++ 程序中使用的,除非你需要调用一个不遵守常量正确性但完全不修改对象的 C 语言函数。如果一个函数有一个const参数,并且用const_cast抛出它的常量,这个函数就打破了它不会修改参数的隐含契约。因此,作为该函数的作者,您有责任确保不会修改对象;否则,您不应该使用const作为参数,因为您将修改对象。

如果您需要在同一个对象上使用const_cast和另一个强制转换操作符,请最后使用const_cast,因为如果您使用后续强制转换,从对象中移除常量可能会导致意外的更改。

static_cast

static_cast操作符对铸造很有用:

  • 浮点类型到整数类型(产生截断的结果)。
  • 整数类型到浮点类型。
  • 枚举类型转换为整数类型。
  • 整数类型到枚举类型。
  • 派生类到基类。
  • 派生类型引用的类型。
  • 指向基类指针的派生类指针。

一般来说,每当将基本类型转换为其他基本类型时,使用static_cast。通常,static_cast 应该是您的第一个强制转换选择,因为它会在编译时执行所有可以执行的检查,所以您不必添加运行时检查来降低程序的速度。

dynamic_cast

dynamic_cast运算符对于通过虚拟继承进行铸造很有用。static_cast可以从派生类强制转换为基类,不管继承是不是虚拟的。然而,假设给你一个类型为 A 的对象,但你知道它实际上是一个类型为 B 的对象——而 B 实际上继承自 A。如果你想将这个对象转换回 B 以使用只有 B 提供的成员函数,你需要使用dynamic_cast

关于dynamic_cast的几件事。首先,它只适用于指针到指针或引用到引用的转换。第二,如果一个对象实际上不是 B(或从 B 派生的类型),它实际上不能将该对象从 A 强制转换为 B。失败的指向指针dynamic_cast返回空值。引用到引用失败抛出std::bad_cast异常。

reinterpret_cast

reinterpret_cast运算符是直接转换,很少有好的用途。它的大多数操作给出了不确定的结果。这实际上意味着,在使用编译器之前,您应该阅读编译器供应商的文档。

正如我们在storageduratingsample中看到的,它的一个用途是将指针转换为一个足够大的整数类型来保存它。这给出了指针的内存地址,这对于调试和跟踪操作非常有用,在这些操作中,您可以将数据转储到日志文件并创建核心转储,但它可能无法轻松运行调试器。你会看到它有时被合法地用于其他目的,但一般来说,它应该被认为是最后的演员阵容(不包括 C 风格的演员阵容,它出现在reinterpret_cast之后)。

C 型演员表

C 型演员表,(例如auto someData = (SomeType)dataOfSomeOtherType;)不是你的朋友。你无疑是从 C# 开始熟悉它的,在那里它非常有用。在 C# 中,如果您尝试使用该语法进行强制转换,而强制转换无效,您将生成 InvalidCastException。发生这种情况是因为 CLR 会跟踪您创建的所有内容的类型,并检测错误的强制转换。

C++ 不会检查你的 C 风格转换是否有效,当然假设它编译了。C++ 只是假设它是。如果演员阵容很差,而你很幸运,你的程序会立即崩溃。如果没有,您将最终得到未知状态的数据,这些数据肯定会以微妙和阴险的方式被破坏。

此外,不同于你可以通过寻找_cast<很容易发现的其他施法,C 风格的施法并不突出。当您快速扫描大量代码时,文本周围的括号看起来就像是一个函数调用,就像是一个转换操作一样。您可以在 Visual Studio 2012 中对此使用正则表达式搜索:\(.*\)[A-Za-z]。即便如此,你仍然放弃了其他演员的所有利益和保护。

C 风格的强制转换可以做的一件事是将对象强制转换为它的一个受保护的或私有的继承基类。你真的不应该这样做,因为如果你需要公共继承,你应该使用公共继承。

简而言之,不要使用 C 风格的转换。

样品

有一个示例铸件示例,演示了多种可能的铸件类型。它包含在这本书的源代码中。为了简洁起见,我在这里省略了它。