06杂项讨论

我们终于抵达了最后一站。本章内含难以归类的准则。一开始的两个条款讨论C++软件开发过程如何设计出能够容纳日后变化的系统。是的,面向对象方法应用于系统构造的一个强大力量就是,它支持日后的变化。这些条款描述了一些特定步骤,你可以用来强化你的软件工事,抵抗这个拒绝停滞的世界带来的刀戟箭弩。

接下来我将验证如何在同一个程序中结合 C 和 C++。这个需求导致语言上的额外考虑,不过 C++毕竟生存于真实世界之中,有时候我们必须面对这样的问题。

最后,我把“C++标准规格”公开之后的各项语言变化做一番摘要整理。在此特别涵盖标准程序库中翻天覆地的大变化(亦请参考条款 E49)。如果你未曾密切跟随标准化的脚步,对于这些变化可能会有很大的惊喜。是的,标准程序库中有许多让人愉悦的东西。

条款 32:在未来时态下发展程序

世事永远在变。

身为软件开发人员,我们可能不是知道得很多,但我们确切知道世事永远在变。我们不一定知道改变的是什么,改变如何到来,改变何时发生,或为什么会发生,但我们真的知道:事情会改变。

好的软件对于变化有良好的适应能力。好的软件可以容纳新的性质,可以移植到新的平台,可以适应新的需求,可以掌握新的输入。软件具备如此的弹性、健壮性、可信赖度,并非是天上掉下来的礼物,而是那些“即使面对今天的束缚,仍然对明天可能的需求念兹在兹”的设计者和实现者共同努力的结果。把目光摆在未来时态的程序员,才写得出这种软件——可优雅接受变化的软件。

所谓在未来时态下设计程序,就是接受“事情总会改变”的事实,并准备应因之道。也许程序库会加入新的函数,导致新的重载(overloadings)发生,于是导致潜伏的歧义(模棱两可)函数发作(见条款 E26)。也许继承体系会加入新的classes,致使今天的 derived classes 成为明天的 base classes。也许新的应用软件会出现,函数会在新的环境下被调用,而我们必须考虑那种情况下仍能正确执行任务。记住,程序的维护者通常都不是当初的开发者,所以设计和实现时应该注意到如何帮助其他人理解、修改、强化你的程序。

要做到这件事情,办法之一就是以 C++“本身”(而非只是注释或说明文件)来表现各种规范。举个例子,如果某个 class 在设计时绝不打算成为 derived classes,那么就不应该只是在头文件的 class 上端摆一行注释就好,而是应该以C++语法来阻止派生:条款 26告诉你怎么做。如果一个 class 要求其所有对象实体都必须于heap 内产生,那么请不要只是告诉 clients 那么做,应该以条款 27厉行这项约束。如果 copying 和 assignment 对某个 class 没有意义,我们应该将其 copy constructor 和 assignment operator 声明为 private(见条款 E27)。C++提供了很大的威力、弹性、表现力。使用这些语言特征来厉行你的设计吧。

既然知道事情总会改变,那么就请在这演化速度有如浪淘沙的软件世界中写一些抗变性比较高的 classes吧。是的,请避免“demand-paged”式的虚函数,那会使你习惯于“不让任何函数成为 virtual,除非有人需要”。你应该决定函数的意义,并决定它是否适合在 derived classes 内被重新定义。如果是,就把它声明为virtual,即使眼前并没有人重新定义它。如果不是,就把它声明为 nonvirtual,并且不要只为了图某人的方便就改变其定义。请确定你所做的改变对于整个 class 的上下关系乃至于它所表现的抽象性是合理的(见条款 E36)。

请为每一个 class 处理 assignment和 copy construction动作,即使没有人使用那样的动作。现在没有人使用,并不意味将来都没有人使用(见条款 E18)。如果这些函数不易完成,请将它们声明为 private(见条款 E27),那就不会有任何人不经意调用“编译器自动产生,行为却错误”的版本(这常常发生于 default assignment operators和 copy constructors身上——见条款 E11)。请不要做出令人大吃一惊的怪异行为:请努力让 classes 的操作符和函数拥有自然的语法和直观的语义。请和内建类型的行为保持一致:如果有疑惑,不妨看看ints 有怎样的表现。

记住,任何事情只要有人能够做,就会有人做。他们会抛出 exceptions,他们会将对象自我赋值,他们会在尚未获得初值前就使用对象,他们会给对象初值却从不使用它,他们会给对象过大的值,他们会给对象过小的值,他们会给对象 null 值。通常,只要编译没问题,就会有人做。所以,请让你的 classes 容易被正确地使用,不容易被误用。请接受“客户会犯错”的事实,并设计你的 classes 有预防、侦测或甚至更正的能力(见条款 33和条款 E46)。请努力写出可移植代码。这并不会比写出不可移植代码更困难,只有某些罕见情况会显著影响性能,使我们不得不调整为不具移植性的架构(见条款 16)。即使程序被设计用于定制型硬件,通常也可能有移植的需求,因为硬件库存量往往数年内便会到达不动如山的窘境。撰写具有移植性的代码,你便能够轻易转换平台,放大客户群,并夸耀说自己支持开放系统。如果你对操作系统押错宝,具有移植性的代码也可以使你不至于全盘皆输。

请设计你的代码,使“系统改变所带来的冲击”得以局部化。尽可能采用封装性质,尽可能让实现细目成为 private(见条款 E20)。如果可用,就尽量用匿名的namespaces 或文件内的 static 对象和 static 函数(见条款 31)。尽量避免设计出 virtual base classes,因为这种 classes 必须被其每一个 derived class(即使是间接派生者)初始化(见条款 4和条款 E43)。请避免以 RTTI 作为设计基础并因而导致一层一层的 if-then-else 语句(见条款 31;条款 E39对此有良好措施):因为每当 class 继承体系一有改变,每一组这样的语句都得更新,如果你忘了其中一个,编译器不会给你任何警告。这些都是广为人知且常被提起的忠告,但大部分程序员还是掉进“现在式”的泥淖中。不幸的是许多书籍作者也缺乏高瞻远瞩。看看这位 C++专家提出的忠告:

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值