高效的代码重用是良好的软件工程中重要的一部分。为了演示如何更好地通过使用标准库算法而不是手工编写,我们再次考虑先前的问题。演示通过简单利用标准库中已有的算法来避免的一些问题。

Problem

JG Question

1. 最广泛使用的C++库是什么?

Guru Question

2. 首先,在GotW #2中有多少陷进是可以避免的,如果程序员只是用以下方法替代显示的基于迭代器的for循环:

     (a)一个基于范围的for循环?

     (b)一个标准库算法调用?

(注意:和GotW #2一样,不要改变函数的语义,即使它们有些地方是可以改进的)

为了简单说明,下面是大部分都已经修正了的函数:

string find_addr( const list<employee>& emps, const string& name ) {
for( auto i = begin(emps); i != end(emps); ++i ) {
if( i->name() == name ) {
return i->addr;
}
}
return "";
}

Solution

1. 最广泛使用的C++库是什么?

     在每个平台上的实现的C++标准库。

2. (a)使用基于范围的for循环可以避免多少GotW #2中的陷进?

     在GotW #2中,机灵的读者可能会说:“为什么你不使用基于范围的for循环?”的确,为什么不?它还会解决一些临时对象问题,不要介意它写的那么简单。

比较先前没有改进的显示迭代器循环:

for( auto i = begin(emps); i != end(emps); i++ ) {
if( *i == name ) {
return i->addr;
}
}

和使用基于范围的for循环(如果你记得写上const auto&还会加分)

for( const auto& e : emps ) {
if( e == name ) {
return e.addr;
}
}

表达式e==name和return e.addr在可能会在实际的临时对象方面都没有变化。但是裸循环代码中的不论是=引发的临时对象(回想一下:它没有),还是end()重新计算且应该保存在循环外(回想一下:可能不会,但也许),还是i++应该被重写成++i(回想一下:它应该这么做),这些问题在基于范围的循环中都得到了解决。这就是清晰代码的能力,同时使用了更高的抽象。

使用基于范围的for循环的关键优点是增了代码的抽象层次和信息密度。考虑一下:下面两行代码中,在不继续阅读接下来的代码时,你能想到什么:

for( auto i = begin(emps); i != end(emps); i++ ) {   // A

for( const auto& e : emps ) {                        // B

乍看起来,A行和B行都传达了相同的信息,但却不是这样。当看到A行时,你所知道的所有就是那是个循环,使用迭代器迭代了emps。我们如此习惯于于A,以致于我们眼睛次要的视觉都趋于在我们脑子里将它“自动补全”为“一个顺寻访问emps元素的循环”,且我们的自动补全通常是对的,除了当它不是:那是个++还是s+=2的循环步幅?在循环体内索引被修改了?我们次要的视觉可能会出错。

另一方面,B行传达了更多的信息给读者。当你看到B行时,在没有检查循环体的情况下你能确信的知道,它是一个顺序访问emps元素的循环,除此之外,你还简化了循环控制,因为没有简洁实用迭代器的必要。所有的这些都提升了代码的抽象层次,这是个好事。

要注意一点,在GotW #2的讨论中,裸for循环在没有通过追加附加的变量执行额外的计算(一个默认的构造函数紧接着一个赋值操作,而不只是一个构造函数)而导致代码更为复杂的情况下,是没有很自然地允许合并到单个return语句的。这对于基于范围的for循环来说还是一样的,因为它依旧是两条return语句在不同的范围内。

2. 。。。使用标准库算法调用?

在没有其他的改变前提下,简单实用标准的find算法做了基于范围for的所有事情,且又避免了不必要的临时对象。

// Better (focusing on internals)
//
string find_addr( /*...*/ ) {
const auto i = find( begin(emps), end(emps), name ); // TFTFY
return i != end(emps) ? i->addr : "";
}

这和基于范围的for版本一样,都消除了相同的临时对象,并且它进一步加强了抽象的层次。我们一眼就能看出,这是一个顺序访问emps元素的循环,但在上一层我们知道是在尝试find一些东西然后返回第一个匹配(如果存在)元素的迭代器。我们依旧间接使用了迭代器,但只是一个一次性的迭代器对象,而不是像原始for版本中的那样对迭代器进行算术运算。

更进一步来说,我们消除了整个循环嵌套域,并且将函数拉平到了一行进行这样简单的函数调用,这是基于范围的for做不到的。为了演示这里一些更基本的要点,注意在拉平函数是还做了其他什么。现在,因为return语句都在一个作用域范围上(可能只是因为我们消除了循环体),我们可以选择地组合它们。当然也可以依旧写成
if( i != end(emps) ) return i->addr; else return “”;
     这样,可以一行或者两行或者四行,但是我们没有必要这么做。为了清晰起见,这里的重点不是减少return语句为目的,它也不该是,并且如我们在GotW #2所说,“单退出”总是有缺陷的。这里的重点是使用算法经常可以简化我们的代码不只是一个显示循环,甚至一个rang-for循环。不仅直接通过删除额外的间接对象和额外的变量以及循环嵌套,而且经常也允许在周边代码进行额外的简化。

上述代码在使用一个string比较employee时依旧可能会引发临时对象,如果我们进一步使用一个自定义的比较函数子的find_if的话,那么我们也将比较产生的临时对象都消除了。假设有一些比如employee::name()是可用的就像在GotW #2中一样,将这个于其他的修改组合起来,我们得到了:

// Better still (complete)
//
string find_addr( const list<employee>& emps, const string& name ) {
const auto i = find_if( begin(emps), end(emps),
[&](const auto& e) { return e.name() == name; } );
return i != end(emps) ? i->addr : "";
}

Summary

当你有一个或者可以编写一个合适的算法来完成我们想要的,倾向于使用算法而不是显示的循环。它可以提升我们代码的抽象层次和清晰度。Scott Meyers在Effective STL中的建议依旧是有效的。尤其是现在有着以前没有的lambda。

Guideline:相对于显示的循环,倾向于选择使用算法。算法调用经常可以使代码更清晰且减少复杂度。如果没有合适的算法,那就编写一个。因为我们可能会再次使用它。

优先使用已经存在的库代码而不是自己从无到有地手动编写。越是广泛地使用库,对于许多常见的需求,它就更可能是精心设计的,预调试和预优化的。还有什么比标准库更广泛使用的呢。在你的c++程序中,你的标准库的实现是你可能会使用最广泛使用的库代码。这有助于不管是库的设计还是实现:它所有的代码的目的是被使用和重用,并且很多思想都已经设计到了这些特性中,包括像find和sort这样的标准算法。库的实现者在库的效率、可用性和所有的注意事项方面都下了很大功夫,因此你没有必要---包括执行优化,应该从不依赖应用程序级别的代码,比如使用不可移植的OS或者特定CPU的优化。

因此,总是倾向于重用代码,特别是算法和标准库。要跳出“我编写那些代码只是因为我能”的陷阱。

Guideline:重用代码,特别是标准库代码,而不是自己手动编写自己的版本,因为它更快,更简单,更安全。

原文地址:http://herbsutter.com/2013/05/16/gotw-3-solution-using-the-standard-library-or-temporaries-revisited/

[译]GotW #3: Using the Standard Library (or, Temporaries Revisited)的更多相关文章

  1. [译]The Python Tutorial#11. Brief Tour of the Standard Library — Part II

    [译]The Python Tutorial#Brief Tour of the Standard Library - Part II 第二部分介绍更多满足专业编程需求的高级模块,这些模块在小型脚本中 ...

  2. [译]The Python Tutorial#10. Brief Tour of the Standard Library

    [译]The Python Tutorial#Brief Tour of the Standard Library 10.1 Operating System Interface os模块为与操作系统 ...

  3. Python语言中对于json数据的编解码——Usage of json a Python standard library

    一.概述 1.1 关于JSON数据格式 JSON (JavaScript Object Notation), specified by RFC 7159 (which obsoletes RFC 46 ...

  4. C++ Standard Library

    C++ Standard Library *注:内容主要是对參考1的学习记录.知识点与图片大都来源于该书, 部分知识点与图片来源于參考2. 详细參考信息,见最下方參考. * C++98中新支持的语言特 ...

  5. C++11新特性——The C++ standard library, 2nd Edition 笔记(一)

    前言 这是我阅读<The C++ standard library, 2nd Edition>所做读书笔记的第一篇.这个系列基本上会以一章一篇的节奏来写,少数以C++03为主的章节会和其它 ...

  6. Python Standard Library

    Python Standard Library "We'd like to pretend that 'Fredrik' is a role, but even hundreds of vo ...

  7. Macro definition of snprintf conflicts with Standard Library function declaration

    Macro definition of snprintf conflicts with Standard Library function declaration 即将此处的宏定义注释掉,因为在VS2 ...

  8. 【概念的辨异】—— ISO C 与 POSIX C(C standard library 与 C POSIX library)

    ISO C 表示 C Standard Library,也就是 C 标准库. 二者的主要区别在于: POSIX 是 C 标准库的超集(也即是从内容上,C 标准库是 POSIX 库的一部分,POSIX ...

  9. Swift Standard Library Reference.pdf

    Swift Standard Library Reference.pdf 下载地址 http://download.csdn.net/detail/swifttrain/7446331 自己的Mark ...

随机推荐

  1. ###《Effective STL》--Chapter4

    点击查看Evernote原文. #@author: gr #@date: 2014-09-14 #@email: forgerui@gmail.com Chapter4 迭代器 Topic 26: i ...

  2. ASP.NET Web API 使用记录

    WebAPI采用REST架构,用的是无状态的HTTP协议.Web Service则是SOAP协议,比较重量级. 推荐阅读:Difference between WCF and Web API and ...

  3. 【开发】Dialog 对话框

    提示:Dialog 继承自 Panel,有大量的方法在 Panel 中已被定义,可以复用. Dialog API:http://www.jeasyui.net/plugins/181.html Pan ...

  4. SQL Constraint/Index

    1.SQL Constraint Integrity Constraints are used to apply business rules for the database tables. The ...

  5. poj 2533 Longest Ordered Subsequence 最长递增子序列

    作者:jostree 转载请注明出处 http://www.cnblogs.com/jostree/p/4098562.html 题目链接:poj 2533 Longest Ordered Subse ...

  6. linux shell编程学习笔记(二) --- grep命令

    Linux系统中grep命令是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹 配的行打印出来.grep全称是Global Regular Expression Print,表示全局正则表达 ...

  7. 安装Debian 7.8 过程,以及该系统的配置过程

    最近,我给自己的电脑安装了Debian操作系统,版本7.8 stable.Debian我相信大家都能了解,它是Ubuntu的基础,Ubuntu是基于Debian的unstable开发的.我安装这个系统 ...

  8. windows 8.1 pro X64安装中断

    用PE安装windows 8.1 pro X64 ISO镜像,快完成的时候卡在蓝色背景那不动了,等待了大概30min强制重启了. 奇怪的是,居然进去了,不过很慢.配置了一段时间终于看到桌面了,关机,失 ...

  9. 纯原生js移动端城市选择插件

    接着上一篇纯js移动端日期选择插件,话说今天同事又来咨询省市县联动的效果在移动端中如何实现,还是老样子,百度上一搜,诶~又全是基于jquery.zepto的,更加可恨的是大多数都是PC版的,三个sel ...

  10. C#多线程(一)

    一.定义与理解 1.定义 线程是操作系统分配CPU时间片的基本单位,每个运行的引用程序为一个进程,这个进程可以包含一个或多个线程. 线程是进程中的执行流程,每个线程可以得到一小段程序的执行时间,在单核 ...