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

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. 取A表数据,关联B表任意一条数据

     表A=================== AID, AName 1 jack 2 mary 3 lily 表B================== BID, AID, BName 1 1 aaa ...

  2. JavaScript高级程序设计(七):JavaScript中的in关键字

    in 使用点一: 在js中,for--in用于遍历一个对象的属性,把对象的属性名和属性值都提出来. var obj = { "key1":"value1", & ...

  3. getScript 按需加载javascript

    $('input:button:first').click(function(aaa) { $.getScript('new.js', function() { alert('Script loade ...

  4. 严重: Exception starting filter struts2 java.lang.NullPointerException (转载)

    严重: Exception starting filter struts2 java.lang.NullPointerException at com.opensymphony.xwork2.util ...

  5. C语言 goto, return等跳转

    C语言 goto, return等跳转 Please don't fall into the trap of believing that I am terribly dogmatical about ...

  6. DB2数据库中提高INSERT性能详解

        分类: Linux INSERT 处理过程概述 首先让我们快速地看看插入一行时的处理步骤.这些步骤中的每一步都有优化的潜力,对此我们在后面会一一讨论. 在客户机准备 语句.对于动态 SQL,在 ...

  7. memcached 在windows下安装及启动

    memcached 在windows下安装及启动   Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数, ...

  8. Websocket协议之握手连接

    Websocket协议是为了解决web即时应用中服务器与客户端浏览器全双工通信的问题而设计的,是完全意义上的Web应用端的双向通信技术,可以取代之前使用半双工HTTP协议而模拟全双工通信,同时克服了带 ...

  9. gwt 创建 超链接cell (HyperTextCell)

    package com.cnblogs.hooligen.client; import com.google.gwt.cell.client.AbstractCell; import com.goog ...

  10. 基于BOOST 实现并发服务器框架

    一:设计思路 本服务器框架使用 UDP 传输协议,程序柱线程等待客户端数据,并将数组存取队列缓冲区.另外可开启多个工作线程,工作线程可以依据具体项目实现不同的功能 ,例如可以将队列缓冲区中的数据逐个取 ...