刘未鹏|C++的罗浮宫

Knowledge sharing is the best reuse

刘未鹏ID:pongba
888555次访问,排名36好友14人,关注者173
兴趣:人工智能、机器学习、知识发现,认知科学。
pongba的文章
原创 101 篇
翻译 8 篇
转载 0 篇
评论 1734 篇
刘未鹏的公告
除非特别声明,本站采用Creative Commons License许可。转载请保留作者、出处。非商业。

FeedSkyFeedBurner
或者用 鲜果 GR 抓虾 订阅。

CSDN Blog暂时不支持RSS全文输出,对此感到不便的朋友可以使用强大的greasemonkey脚本:GReader Preview Enhanced(链接),该脚本支持在GReader里面直接打开全文页面。

P.S. 我经常出没于TopLanguage讨论组

《C++的罗浮宫》5年选集(下载)

——知识分享是最大的复用


讨论问题请到TopLanguage小组

TopLanguage


gtalk/msn(邮件请发送到gmail邮箱)

pongba@gmail.com
pp_liu@msn.com

豆瓣 饭否 美味书签

搜索C++的罗浮宫上的内容(不要回车,点击Go)

twitters

books I've translated




这个Blog上都写了哪些东东

最近评论
chenjing24:可以!
xingranliuyun:原来这里还有一份,在这里发些少于一百字的回复吧。

关于争吵。其实吵也就吵了,无所谓的,不吵架也是很奇怪的。

那个关于chrome的文章里提到了'm'键的使用。其实m键的功能对我来说有些无所谓,因为我一直都用浏览器看的,就算看到不感兴趣的主题,也不会认为是被打扰了。

因为以前一直看pongba的blog,有一天看到toplang……
Googol:爬过来又看了一遍,顺手把提到的基本书加到豆瓣的想读里。

ps 我不认为“存在即合理”是同义反复。当然这句话本身是高度概括的,甚至是抽象空洞的,因为没有表述请是什么情况下的存在和什么情况下的合理,每个人都可以把自己的理解往里面套。但存在和合理肯定是两个不同的概念,存在是一个物理上的属性,而合理则是逻辑上的。
zwp:这只是一个感想。
nirvash:一心称念弥陀圣号!
文章分类
收藏
相册
其它图片
文章中的图片
我的大头贴
C++
Andrei Alexandrescu
Andrew Lumsdaine
Bjarne Stroustrup
boost
C++ Standard Commitee
Doug Gregor
Hans J. Boehm
Jaakko Jarvi
Jeremy G. Siek
Matthew Wilson
newsgroups
boost.Developer
boost.User
comp.lang.c++.moderated
comp.std.c++
TopLanguage
Open Source
codeplex
Google AJAX Search API
Google Code Prettify
Google Web Toolkit
MS shared source initiative
notepad++
STLSoft
不认识的朋友们
fatalerror99
Glacier
realazy
SpiritEpic
TK
Yelz
YongSun
余晟|乱象&乱想
刘慈欣
吴欣安(atppp)
姬十三
张志强|阅微堂
许式伟
阮一峰
高远
鲍志云
其它
科学松鼠会
科学美国人
科幻世界
认识的朋友们
alai
chenyufei
dd
duguguiyu|Venus神庙
Googol
Joyfire
littlestone
lxwde
Matrix67
realfun
soloist
Tinyfool
windstorm
云风
冯大辉(Fenng)
刘新宇
刘江@图灵
史晓明
周星星
周筠@博文视点
孟岩
张振
徐宥|4G Spaces&Web 2.3
方舟@博文视点
李笑来|Pure Pleasure
杨文博
王信文|地球没有好朋友
荣耀
莫华枫
袁泳(g9)|负暄琐话
谢东升
陈冀康@华章
存档
订阅我的博客
XML聚合  FeedSky
订阅到鲜果
订阅到Google
订阅到抓虾
订阅到BlogLines
订阅到Yahoo
订阅到GouGou
订阅到飞鸽
订阅到Rojo
订阅到newsgator
订阅到netvibes

翻译 《Exceptional C++ Style》开放样章译稿收藏

新一篇: 关于C++泛型编程的一些杂感 | 旧一篇: Those Old Golden Times (怀念一位退学的朋友)

感谢老朋友谢轩(《Symbian OS Explained》译者)无私提供原译稿,这是调整过的版本。因为是原公开样章,所以放在blog上。

34 索引表

难度5

 

索引表确实是一种有用的惯用法(idiom),而且是一种值得去了解学习的技术。但我们如何才能有效地实现这一技术呢……等等,应当不仅是“有效”,“完美”怎么样?

 

JG问题

1.   谁会受益于清晰易懂的代码?

Guru问题

2.   以下代码展现了在已有容器中创建索引表的一种有趣且有用的惯用法。如需更详细的解释,请参考其原文[Hicks00]

评价这段代码并找出:

a)        像无效语法或不可移植的风格习惯之类的“机械”错误。

b)       在风格上可以作哪些改进,使代码的清晰度、重用性和可维护性都得到改善。

 

// sort_idxtbl(…)的作用是排列一个索引数组

#include <vector>

#include <algorith>

template <class RAIter>

struct sort_idxtbl_pair

{

    RAIter it;

    int i;

    bool operator<( const sort_idxtbl_pair& s )

    { return (*it) < (*(s.it)); }

    void set( const RAIter& _it, int _i ) { it=_it; i=_i; }

    sort_idxtbl_pair() {}

};

template <class RAIter>

void sort_idxtbl( RAIter first, RAIter last, int* pidxtbl )

{

    int iDst = last-first;

    typedef std::vector< sort_idxtbl_pair<RAIter> > V;

    V v( iDst );

    int i=0;

    RAIter it = first;

    V::iterator vit = v.begin();

    for( i=0; it<last; it++, vit++, i++ )

        (*vit).set(it,i);

    std::sort(v.begin(), v.end());

    int *pi = pidxtbl;

    vit = v.begin();

    for( ; vit<v.end(); pi++, vit++ )

        *pi = (*vit).i;

}

main()

{

    int ai[10] = { 15,12,13,14,18,11,10,17,16,19 };

    cout << "#################" << endl;

    std::vector<int> vecai(ai, ai+10);

    int aidxtbl[10];

    sort_idxtbl(vecai.begin(), vecai.end(), aidxtbl);

    for (int i=0; i<10; i++)

        cout << "i=" << i

        << ", aidxtbl[i]=" << aidxtbl[i]

    << ", ai[aidxtbl[i]]=" << ai[aidxtbl[i]]

    << endl;

    cout << "#################" << endl;

}

 

解决方案

 

清晰度

1.  谁会受益于清晰易懂的代码?

简而言之,对所有人都有好处。

首先,清晰的代码更易于调试,也正因为清晰,所以代码在第一时间的错误也就少很多,就算目光再短浅,编写清晰的代码也至少可以让你的生活更轻松一些。(相关案例,请参考第27条款围绕示例27-3的讨论。)此外,当你一个月或一年之后重读你的代码时(如果你的代码当初没问题并投入了实际使用的话,这一环节通常是免不了的),你就会发现更容易“重拾”那些清晰的代码,明白代码都干了些什么。绝大多数程序员觉得要在头脑中记住代码的全部细节并保持哪怕只是几周时间都是很困难的,尤其是在转向其它的工作之后,要记住这些就更加困难了。经过几个月乃至几年之后,即便是重读自己以前写的代码,也很容易觉得那似乎是一个陌生人写的(只不过那个“陌生人”恰好跟你有同样的个人编码风格)。

利己方面已经说得够多了。让我们来看看有利于别人的方面:代码维护者也将获益于代码的清晰性和可读性。毕竟,要将代码维护好首先得“投入(grok)”代码。罗伯特.海因莱因(Robert Heinlein)杜撰了“grok”一词,意指深入而完整地理解;在此处,这个词还包含有理解代码本身内在的工作方式、代码的副作用以及其与其它子系统的交互方式的意思。总而言之,在没有完全理解一段代码的情况下就贸然去修改它太容易引入新的错误了。清晰、可理解的代码更容易让人投入其中,因此,对于这种代码的修补就变得不那么脆弱、危险,而且也不太容易无意间引入不想引入的副作用。

然而,最重要的一点是,由于以上这些原因,你的最终用户将得益于清晰、可理解的代码:这种代码从一开始错误就很少;更容易被正确地维护,而且在维护过程中也不至于引入同样多的错误。

 

指导方针:

一般来说,优先考虑编写清晰、正确的代码。

 

深入剖析索引表

2.        以下代码展现了在已有容器中创建索引表的一种有趣且有用的惯用法。如需更详细的解释,请参考其原文[Hicks00]

评价这段代码并找出:

a)         像无效语法或不可移植的风格习惯之类的“机械”错误。

b)         在风格上可以作哪些改进,使代码的清晰度、重用性和可维护性都得到改善。

 

请允许我重复一遍:这些代码展示了一种有趣且有用的惯用法。我常常发现必须以不同的方式访问相同的容器,例如按照不同的排序准则来看待同一个容器中的元素。所以说这样的方法的确是很有用的:以一个主容器(譬如vector<Employee>)保存实际数据,以若干副容器保存指向主容器中元素的迭代器,这样一来我们就能支持各式各样的访问方式(例如,set<vector<Employee>::iterator, Funct>,其中Funct是用以间接比较Employee对象的仿函数,于是就可以产生不同于对象在vector中物理存储顺序的排序)。

话虽如此,风格也是很重要的。原作者爽快地允许我将他的代码作为相关案例,而我也并非在此对他的代码吹毛求疵;而只不过是通过剖析和批评已发布代码的方式来阐释编码风格原则罢了,很早以前诸如P.J.Plauger等人就已经这么做了。我从前评论过其它人发布的东西,同时也让别人来批评我的东西,而且我相信这一做法会得到更多人的认同和效仿。

说完了所有这些,让我们来看看究竟能对给出的代码案例作哪些改进。

更正“机械”错误

a)       像无效语法或不可移植的风格习惯之类的“机械”错误。

建设性的批评的首要方面就是代码中的“机械”错误。像下面列出的这些“机械”错误在大多数平台上是无法通过编译的。

#include <algorith>

1.        正确拼写标准库头文件名。在这个例子中,头文件<algorithm>被误作<algorith>。我首先猜测,这也许是因为原先代码的测试环境是一个8字符文件名的系统的缘故,但即便是我的老版本Windows(基于8.3文件名系统)上的老版本VC++也无法编译这样的代码。就算是在那些限制颇多的文件系统上,编译器本身也是被要求能支持任何标准长度的头文件名的(即便编译器只不过是在背后悄悄将它映射成较短的文件名(或根本不映射到文件上))。

接下来,考虑:

main()

2.        正确地定义main函数。这种无修饰的main函数原型从来都不是标准C++[C++98]风格的,虽说它也可以作为一个合法的编译器扩展特性(前提是编译器得给出警告)。这种main函数原型在C99之前是有效的,因为当时的C里面允许所谓的“隐式int声明”,但这在C++C++里从来就不允许隐式int)和C99[C99]C99甚至果断地将这一特性彻底从标准中剔除掉了)中都是非标准的。在C++标准中,请参看:

§3.6.1/2:可移植代码必须将main定义为int main()int main(int,char*[])两种形式中的一种。

§7/7 脚注78,和§7.1.5/2脚注80:隐式int是被禁止的。

附录 C(兼容性),对7.1.5/4的注释:明确指出main()这种形式在C++中是无效的,必须写作int main()

 

指导方针:

不要依赖隐式int;这不是符合标准的可移植的C++。特别地,“void main()”或光是“main()”从来就不是标准C++写法(虽然仍有很多编译器将它们作为扩展加以支持)。

 

cout << “#################” << endl;

3.        永远记得#include你所需要的类型定义的头文件。这个程序使用了coutendl但却没有#include<iostream>。那为什么这在代码原作者的系统上能工作呢?这是因为C++标准头文件会互相#include,但不像CC++并没有指定哪些标准头文件#include哪些其它标准头文件。在这个案例中,程序有#include <vector><algorithm>,而在原来的那个系统上,也许恰好某个头文件间接地#include <iostream>了。这在原代码所使用的特定的库实现上或许是行得通的,甚至在我这恰巧也能正常工作,但它并不是可移植的,而且也不是种好风格。

4.        遵循《More Exceptional C++[Sutter02]的条款39中关于使用名字空间(namespace)的原则。coutendl而言,程序必须以std::限定它们,或者像这样写:using std::cout; using std::endl;。不幸的是,忘记名字空间域限定符的情况仍然很普遍。我得赶紧指出,这段代码的原作者对vector