《C++编程调试秘笈》——2.3 处理类型的正确方式

2.3 处理类型的正确方式

C++编程调试秘笈
现在,如果我们创建一个Time类,在内部实现中隐藏了从什么时间开始,以及用什么时间单位(秒、毫秒等)进行测量等细节,上面这些杂七杂八的问题就可以轻松得以避免。这种方法的一个优点是如果我们错误地传递了其他日期数据,而不是传递了时间(现在用Time类型表示),编译器马上就能捕捉到这种错误。这种方法的另一个优点是,如果Time类当前是用毫秒实现的,并且以后为了提高精度用微秒表示,我们只需要编辑一个类,修改内部实现的细节,而不会影响其余的代码。

因此,我们怎样才能在编译时而不是在运行时捕捉类型错误呢?我们首先可以用一个单独的类表示每种类型的数据。我们用int表示整数,用double表示浮点数,用std::string表示文本,用Date表示日期,用Time表示时间,对于其他类型的数据也都用一个单独的类表示。但是,只采用这种做法仍然是不够的。假设我们有两个类Apple和Orange,并有一个期望接受一个Orange类型的参数的函数:

但是,我们可能不小心向它提供了Apple类型的对象:

在有些情况下,这样的代码可以通过编译,因为C++编译器试图向我们提供帮助。只要可能,它会把Apple平静地转换为Orange。这可能通过以下两种方式发生。

(1)如果Orange类具有一个只接受一个Apple类型的参数的构造函数。

(2)如果Apple类具有一个可以把它转换为Orange的操作符。

当Orange类具有下面这样的定义时,就会发生第一种情况:

它甚至可以像下面这样:

即使在最后这个例子中,构造函数看上去像是具有两个输入,但它也可以只用一个参数就可以被调用,因此它也可以隐式地把Apple转换为Orange。这个问题的解决方案是用关键字explicit声明这类构造函数。这种做法可以防止编译器执行自动(隐式)转换,这样我们就可以迫使程序员在期望接受Orange的地方必须使用Orange:

第二个例子需要对应地修改为:

另一种让编译器知道怎么把Apple转换为Orange的方法是提供一个转换操作符:

这个操作符在此处的出现是非同寻常的,说明程序员用一种明确的方式向编译器提供了一种把Apple转换为Orange的方法,它并不是什么错误。因此,对所有接受一个参数的构造函数用关键字explicit进行声明,这是值得推荐的做法。一般而言,隐式转换的所有可能性都是不好的思路。因此,如果想按照上面这个例子一样在Apple类中提供一种把Apple转换为Orange的方法,下面是一种更好的方法:

在这个例子中,为了把Apple转换为Orange,需要采用下面的方式:

另外还有一种方法可以混合不同的数据类型,即使用枚举(enum)。考虑下面这个例子:假设我们定义了下面这两个枚举,分别表示一周中的某天以及月份。

这些常量实际上都是整数(例如,C内置的int类型)。如果我们有一个期望接受一周中的某天作为参数的函数:

下面这个调用将会在不产生任何警告的情况下通过编译:

在运行时,我们能够采取的措施不多,因为JAN和MON都是与1相等的整数。捕捉这类缺陷的方法是不使用创建整数的“单纯功能”枚举,而是使用创建新类型的枚举:

在这种情况下,期望接受一周中的某天为参数的函数将被声明为:

像下面这样试图用一个Month值调用这个函数:

将会产生编译错误:

这正是我们在这个例子中期待产生的效果。

但是,这种方法具有一个消极因素。在这个例子中,用枚举创建整型常量时,我们可以编写如下的代码:

但是当我们使用枚举创建新类型时,如下面的写法:

就无法通过编译。因此,如果我们需要迭代枚举的值,可以像原来一样使用整数。

当然,任何规则都有例外,有时候程序员有理由编写像Variant这样的类,允许进行隐式类型转换,以满足特定的需要。但是,绝大多数时候应该完全避免隐式类型转换,这就允许我们充分利用编译器检查不同变量类型的功能,早期(即在编译时)捕捉潜在的错误。

现在,假设我们已经尽自己所能使用了类型安全。遗憾的是,除了bool和char类型之外,每种类型可能包含的值的数量都是天文数字,通常只有一小部分值是合理的。例如,如果我们使用double类型表示股票的价格,可以很合理地确定股票的价格将在0到10 000之间波动(唯一的例外是Berkshire Hathaway公司的股票,它

的主人Warren Buffet显然并不相信把股票价格保持在合理范围内是个好主意,因此他从不对股票进行除权,在本书写作之时这个股票的价格是每股10万美元)。但即使是Berkshire Hathaway这样的股票,它的价格仍然只使用了double类型的很小一部分,因为double的范围高达10308,并且还包含了完全不适合表示股票价格的负数。由于大多数类型只有一小部分值是合理的,因此总是存在一些只能在运行时才能诊断的错误。

事实上,C语言的大多数问题,例如指定了越界索引,或通过指针运算不恰当地访问内容,只能在运行时才能得到诊断。由于这个原因,本书的剩余部分主要专注于讨论捕捉运行时错误。

本章所讨论的在编译时诊断错误的规则如下。

禁止隐式类型转换:用关键字explicit声明一个接受1个参数的构造函数,并避免使用转换操作符。
用不同的类表示不同的数据类型。
不要使用枚举创建整型常量,而是用它们创建新类型。
本文仅用于学习和交流目的,不代表异步社区观点。非商业转载请注明作译者、出处,并保留本文的原始链接。

时间: 2024-09-20 06:08:35

《C++编程调试秘笈》——2.3 处理类型的正确方式的相关文章

《C++编程调试秘笈》导读

前言 C++编程调试秘笈 敏锐的读者可能会根据本书的英文书名<Safe C++>推断出C++编程语言多少是有点儿不安全的.这确实是很灵敏的感觉!并且非常正确.C++语言可能导致程序员出现所有类型的错误,例如访问一个动态分配的数组边界之外的内存,或者从那些从未初始化过的内存进行读取,或者分配了内存但忘了销毁它.简而言之,程序员在使用C++进行编程的时候,会有很大的几率搬起石头砸自己的脚.很可能一切都非常顺利,程序却突然崩溃,或者产生不可理喻的结果,或者出现了计算机术语中称为"不可预料的

《C++编程调试秘笈》——第1章 C++的缺陷来自哪里

第1章 C++的缺陷来自哪里 C++编程调试秘笈 C++语言是非常独特的.虽然实际上所有的编程语言都从其他语言中吸收了一些思路.语法元素和关键字,C++却是吸收了另一种完整的语言,即C语言.事实上,C++语言的创建者Bjarne Stroustrup原先把他的新语言命名为"带类的C".这意味着如果我们已经使用了一些C代码,并且由于某种原因(例如科研或贸易)切换到一种面向对象的语言,就不需要在移植代码方面采取任何措施,只要安装新的C++编译器,就可以对旧的C代码进行编译了,并且效果和原先

《C++编程调试秘笈》——2.2 怎样用编译器捕捉缺陷

2.2 怎样用编译器捕捉缺陷 C++编程调试秘笈现在我们应该已经坚信,只要有可能,就尽量在编译时捕捉错误.但是,怎样才能实现这个目标呢?让我们观察一对例子. 第一个例子是一个Variant类的故事.曾几何时,一家软件公司编写了一个Excel插件.这是一个文件,被Microsoft Excel打开之后向它添加了一些新功能,可以在Excel单元格中被调用.由于Excel单元格可以包含不同类型的数据,包括整数(例如1).浮点数(例如3.1415926535).日期(例如1/1/2000)甚至是字符串(

《C++编程调试秘笈》——第2章 什么时候捕捉缺陷2.1 为什么编译器是捕捉缺陷的最好场合

第2章 什么时候捕捉缺陷 C++编程调试秘笈本文仅用于学习和交流目的,不代表异步社区观点.非商业转载请注明作译者.出处,并保留本文的原始链接. 2.1 为什么编译器是捕捉缺陷的最好场合 C++编程调试秘笈如果在编译时捕捉缺陷与在运行时捕捉缺陷之间进行选择,只要有可能,都应该在编译时捕捉缺陷.这样做的理由有很多.首先,如果一个缺陷是被编译器所检测到的,我们将看到一条文本信息,准确描述了所发生的错误是什么,它是在哪里发生的,发生在哪个文件以及发生在哪一行.(作者在这里可能稍微有点乐观,因为在有些情况

《T-SQL性能调优秘笈——基于SQL Server 2012 窗口函数》——1.1 窗口函数的背景

1.1 窗口函数的背景 T-SQL性能调优秘笈--基于SQL Server 2012 窗口函数 在开始学习具体的窗口函数之前,先了解其背景和内涵,会对后续的学习有所帮助.本节先谈谈窗口函数的背景,解释基于集合方式和基于游标/迭代方式进行查询的不同,以及窗口函数如何对二者的差异进行弥补.最后,本节也提到了窗口函数的替代方法,以及为什么窗口函数会优于其替代方法.注意,尽管窗口函数能非常高效地解决很多问题,但在某些案例中,替代方法会好于窗口函数.第4章会具体谈论对窗口函数的优化,解释在什么情况下,计算

《黑客秘笈——渗透测试实用指南》—第1章1.1节搭建渗透测试主机

第1章 赛前准备--安装 黑客秘笈--渗透测试实用指南 本章将直接探讨攻击系统的配置方法.安全测试最为重要的方面就是有一个可重复的流程.所以,您需要有一套标准化的基准系统.测试工具和测试流程.本章将会讲解配置测试平台的方法,以及本书示例所需的额外工具的安装步骤.只要按照本章的步骤配置测试平台,您就能够重现后续章节中我所提供的案例.演示.好!让我们全力以赴.积极备战吧. 1.1 搭建渗透测试主机 黑客秘笈--渗透测试实用指南 在进行渗透测试的时候,我都会配置两套不同的测试主机.其中一台是Windo

《基于ArcGIS的Python编程秘笈(第2版)》——2.8 添加图层到地图文档

本节书摘来自异步社区<基于ArcGIS的Python编程秘笈(第2版)>一书中的第2章,第2.8节,作者: [美]Eric Pimpler(派普勒) 更多章节内容可以访问"异步社区"公众号查看. 2.8 添加图层到地图文档 在很多情况下都需要把图层添加到地图文档中.制图模块提供了AddLayer()函数来实现这一功能.本节将介绍如何使用AddLayer()函数把图层添加到地图文档中. 2.8.1 准备工作 arcpy.mapping模块提供了在已有的地图文档中添加图层或图层

《黑客秘笈——渗透测试实用指南(第2版)》—第1章1.5节构建渗透测试环境

1.5 构建渗透测试环境 在本书第1版中,收到很多关于为什么要让读者搭建和安装工具而不是创建脚本自动化完成这些工作的评论.主要原因是想让读者亲自经历这些步骤,这些工具都非常重要,搭建和安装的过程将有助于您知道武器库里都有什么工具.以Kali Linux为例,它包括大量工具,而且进行了很好的组织和归类,但是如果不知道这个工具已经安装了或者没使用它进行过单独的攻击,那么在紧急的情况下很难正确使用这个工具. 1.5.1 安装一个渗透测试环境 如果您根据本书第1版建立了测试环境,就可以跳过本节.众所周知

《黑客秘笈——渗透测试实用指南》—第2章2.4节Web应用程序的扫描

2.4 Web应用程序的扫描 黑客秘笈--渗透测试实用指南 网络扫描器.Peeping Tom可大致揭露测试目标的网络构造.在此之后,我们就可以进行Web应用程序扫描了.本文主要围绕Burp Suite程序讨论Web应用程序的扫描方法.确实有许多开源.免费的工具可进行Web应用程序扫描,例如ZAP.WebScarab.Nikto.w3af等程序都很不错.但是我们有必要再次强调一下,渗透测试的工具应当是速度最快.扫描结果最好的测试工具.虽然Burp Suite专业版(http://portswig