﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-О ο o 虫子不乖 o ο О-随笔分类-C++名家对话</title><link>http://www.cppblog.com/ornaking/category/3088.html</link><description>Keep hoping!
Keep trying!
Keep giving!  --- Always Be a Student!</description><language>zh-cn</language><lastBuildDate>Wed, 21 May 2008 01:07:18 GMT</lastBuildDate><pubDate>Wed, 21 May 2008 01:07:18 GMT</pubDate><ttl>60</ttl><item><title>C++名家对话 10</title><link>http://www.cppblog.com/ornaking/archive/2007/10/23/35036.html</link><dc:creator>ornaking</dc:creator><author>ornaking</author><pubDate>Tue, 23 Oct 2007 14:55:00 GMT</pubDate><guid>http://www.cppblog.com/ornaking/archive/2007/10/23/35036.html</guid><wfw:comment>http://www.cppblog.com/ornaking/comments/35036.html</wfw:comment><comments>http://www.cppblog.com/ornaking/archive/2007/10/23/35036.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ornaking/comments/commentRss/35036.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ornaking/services/trackbacks/35036.html</trackback:ping><description><![CDATA[<p>--------------------------------------------------------------------------------</p>
<p>那天一开始和以前一样没什么两样。 </p>
<p>　 我早早就起了床，抓紧时间看了一些技术文章，然后在食堂大厅里吃早饭，去换班的时候还提前了五分钟。这轮班本来就是例行公事，并没什么大事发生。但临近下班时，怪事发生了。距离换班整整还有十分钟，布拉森科沃斯基居然晃了进来，准备接我的班。 </p>
<p>　 真是太阳从西边出来了，布拉森科沃斯基干任何事情都不会提前。但我实在想不出今天他为什么提前。他查看了一些物件后，走过来对我说，&#8220;嗨！弗兰奈尔找你有事，回去时顺便弯一下她住的特等舱。&#8221; </p>
<p>&#8220;哦？什么事？&#8221; </p>
<p>　 &#8220;不知道，&#8221; 布拉森科沃斯基耸了耸肩。接着他又微微一笑，轻轻地拍了拍我的背。&#8220;你最好还是过去吧，&#8221;他补充道，&#8220;这里有我顶着。&#8221; </p>
<p>　 &#8220;是啊，轮到你了，&#8221;我表示同意，接着又叹了口气，出门而去。 </p>
<p>　 当我到弗奈尔的船舱里，门已经打开了。她招手让我进去，身后的门轻轻关上。她的客椅上已坐着另外一位男士。我以前好像没见过他。 </p>
<p>　 &#8220;嗨，嗨&#8221;他的声音很友好，站起来握我的手。 </p>
<p>　 &#8220;这是主管吉尔伯，&#8221;弗奈尔给我们介绍了一下，于是坐了下来。 </p>
<p>　 &#8220;听说您可以去圆屋帮助我们？&#8221;吉尔伯单刀直入。 </p>
<p>　 我看了弗奈尔一下：&#8220;是的，只要我能行。&#8221; </p>
<p>&#8220;哦，我想你肯定行，&#8221;吉尔伯信誓旦旦地说，&#8220;你原先是跟珍妮.卡露莎一块干的吧。是的，她现在在我的部门，而且她对你评价很高，对，非常高。我知道你现在负责的日常工作越来越多，&#8221;他接着说道，&#8220;但在研究现场工作更有挑战性。怎么样？感不感兴趣？&#8221; </p>
<p>　 &#8220;是的。这样很好。我已经要求参加更多跟研究有关的工作。&#8221; </p>
<p>　 &#8220;很好，很好。我相信薪水也很合适。是的，很合适。你当然明白在那里要呆上一段时间，至少等我们有能力恢复这几座建筑物和圆屋之间的正常交通。现在逼不得已才使用这个临时过道。&#8221; </p>
<p>　 他开始问我一大堆问题。我什么也不记得了。脑袋有点飘飘然，但还是高兴地意识到又可以见到珍妮了。真滑稽，我想，这和年轻时交上女朋友有点不一样，跟那时不太一样，那时我刚参加工作，很多年以前了&#8230;&#8230; </p>
<p><br>--------------------------------------------------------------------------------</p>
<p>　 &#8220;肯定有更好的方法，&#8221;我咕哝着。 </p>
<p>　 &#8220;嗨，伙计，怎么了？&#8221;温迪愉快的嗓音在小屋里缠绕。我伸了伸腰决定暂停编码休息一会，于是地鼠般地弓起身来。 </p>
<p>　 &#8220;太好了，&#8221;我回答道，&#8220;你回来了？我以为你要出去两周呢？&#8221; </p>
<p>　 她白了我一眼：&#8220;你去哪里了，木星吗？我已经旅游两周并且度过了周末假！&#8221; </p>
<p>　 &#8220;开心的时候时间过得飞快，&#8221;我偷偷笑了笑。 </p>
<p>&#8220;哼，&#8221;她说道，&#8220;你跟安娜去哪儿了！知道吗？我告诉过你Guru的party会很有趣的，没想到你带着她女儿在外面泡。&#8221; </p>
<p>　 &#8220;诶，前几周我并不知道她是的Guru女儿，&#8221;我抗议道，&#8220;我以为她只是party的一个客人。&#8221; </p>
<p>　 &#8220;吃午饭时你慢慢告诉我。&#8221;她把雨靴塞到桌子下边又换上了便鞋。&#8220;还有，这里怎么了？我进来时气氛有点紧张。&#8221; </p>
<p>　 &#8220;好吧，拉一个椅子过来我会告诉你血淋淋的事实。说不定你有什么新的见解。&#8221;等温迪走进我的小屋，我又坐进椅子里。 </p>
<p>　 &#8220;我在我们的系统上实现一些通讯功能。使用一种很简单的协议。服务端只是返回一系列以逗号作定界符的带引号的字串。因为字串可以带引号，所以我写了一个简单的函数来处理字串，在字串两端加上引号，而字串内部的引号则作转义处理，就像这样，&#8221;我在白书写板上演示给她看： </p>
<p>Input<br>Output</p>
<p>field 1<br>"field 1"</p>
<p>field "with quotes"<br>"field \"with quotes\""</p>
<p>　 &#8220;我写了两个函数，一个用来加引号，另一个用来去掉引号： </p>
<p>std::string quote(const std::string &amp;);<br>void f(std::ostream &amp;o)<br>{<br>o &lt;&lt; quote("whatever");<br>}</p>
<p>std::string unQuote(std::string &amp;);<br>void g(std::istream &amp;i)<br>{<br>std::string input;<br>i &gt;&gt; input;<br>std::string original=unQuote(input);</p>
<p>// use string 'original' ...<br>}</p>
<p>&#8220;不错，很直截了当，&#8221;温迪答道，&#8220;这是典型的你用于文本文件解析器的思路。&#8221; </p>
<p>　 &#8220;是的，是很简单。但如果原始字串很长，我只好拷贝整个字串。我们的服务器软件必须很紧凑，因为它是在我们所制造的那些箱子里运行的。而且，项目组有些成员抱怨这样使用起来显得很笨拙，他们希望可以使用流插入和提取。像这样，&#8221;我用力地在白书写板上写出如下代码： </p>
<p>std::string message;<br>// aStream is an istream subclass<br>aStream &gt;&gt; message;</p>
<p>&#8220;我必须设法将文本放进aStream，再从中提取一个字段的内容到message中。我已经在流缓冲区里折腾了好半天，但还是不怎么行。&#8221; </p>
<p>　 &#8220;用操纵器。&#8221;听到Guru温和的嗓音时，温迪和我跳了起来。 </p>
<p>　 &#8220;什么？&#8221;我问。 </p>
<p>　 &#8220;写你自己的IOStream操纵器，就像Langer和Kreft刚在C++ Repot中所描述的那样[1]。&#8221; </p>
<p>　 &#8220;唔，你是说先知Langer和Kreft？&#8221;我问。 </p>
<p>　 &#8220;鲍勃在开会，我现在没有精力来充当Guru，&#8221;她耸了耸肩。即使她不摆出Guru的派头，我心里还是把她当成Guru。而且她总是在关键时刻神秘地出现！我开始怀疑她是否在附近布置了眼线。 </p>
<p>　 &#8220;啊，&#8221;我说，一边奇怪为什么我的回答总是如此软弱。&#8220;那么怎么写一个IOStream操纵器呢？&#8221; </p>
<p>　 &#8220;有两种方法，取决于你实现的操纵器有没有参数。如果你的实现中要处理一个参数，接下来我会完整地描述给你听。如果你还想知道怎样实现没有参数的操纵器，你可能得再翻翻以前的一些东西了。 </p>
<p>　 &#8220;实现带一个参数的操纵器实际上很简单。你只要简单地创建一个类，在其构造函数中接收适当的参数，并提供插入和提取操作符，操作符内部调用一些操作操纵器的成员函数。比如你要写一个ostream的操纵器来操纵一个整数，可以这样做。&#8221; Guru擦掉白书写板上的字，用她细长好看的字体整洁地写出如下代码： </p>
<p>class myManipulator<br>{<br>int manipValue;<br>public:<br>explicit myManipulator(int i) : manipValue(i) {} // Note it's 'explicit'!<br>friend ostream &amp; operator &lt;&lt;(ostream &amp;os, const myManipulator &amp;mm);<br>};</p>
<p>　 &#8220;使用操纵器很简单，就像使用std::setfill和std::width一样：&#8221; </p>
<p>　 &#8220;编译器将产生一个临时的无名myManipulator对象，使用42来初始化它。接着编译器调用重载的插入操作符。插入操作符对流执行必要的操纵，就像下面这样：&#8221; </p>
<p>ostream &amp; operator &lt;&lt;(ostream &amp;os, const myManipulator &amp;mm)<br>{<br>if (mm.manipValue == 42) {<br>os &lt;&lt; "Life, the universe, and everything";<br>} else {<br>os &lt;&lt; "What was the question, again?";<br>}<br>return os;<br>}</p>
<p>　 我想了一下。&#8220;那么&#8230;&#8221;我小心地问，&#8220;我的ostream操纵器可以使用字符转义和反转义吗？&#8221; </p>
<p>　 &#8220;你可以这么做，也可以分开两个不同的类来实现转义和反转义[2]，escape 及 unescape [2].&#8221;她突然停住了，露出了微妙的变化。这时，鲍勃走了过去，是要去咖啡屋。&#8220;记住，孩子，&#8221; Guru接着说，&#8220;多读、多想先知Kreft和Langer的文章。&#8221; </p>
<p>　 &#8220;是，老师，下午给你看我的结果&#8221;我回答。鲍勃停了下来，他的杯子掉在了地板地毯上。他拿起杯子，没有回头走了。当他在视野里消失之后，温迪，Guru和我交换了一下眼神。是的，我们就想达到这样的效果。 </p>
<p><br>--------------------------------------------------------------------------------</p>
<p>　 主管吉尔伯可没想这么多，至少此时没有。过了一会他接下去说：&#8220;是，是的，你会做得很好。欢迎加入这个小组。&#8221; </p>
<p>　 我们握了握手，我张嘴笑了：&#8220;谢谢。我什么时候调您那边？&#8221; </p>
<p>　&#8220;什么？噢,你已经是了。不用担心行李。你的东西已搬到圆屋了。你值班时他们已经行动了，都办好了。 &#8221; </p>
<p>　 我还没明白过来，已经被带走进了一部停在中转站的轿车。吉尔伯还在愉快地小声交谈时，车门关上了，我们加速驶进了开往圆屋方向的通道。 </p>
<p><br>--------------------------------------------------------------------------------</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/ornaking/aggbug/35036.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ornaking/" target="_blank">ornaking</a> 2007-10-23 22:55 <a href="http://www.cppblog.com/ornaking/archive/2007/10/23/35036.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++名家对话 09</title><link>http://www.cppblog.com/ornaking/archive/2007/04/30/23253.html</link><dc:creator>ornaking</dc:creator><author>ornaking</author><pubDate>Mon, 30 Apr 2007 09:11:00 GMT</pubDate><guid>http://www.cppblog.com/ornaking/archive/2007/04/30/23253.html</guid><wfw:comment>http://www.cppblog.com/ornaking/comments/23253.html</wfw:comment><comments>http://www.cppblog.com/ornaking/archive/2007/04/30/23253.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ornaking/comments/commentRss/23253.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ornaking/services/trackbacks/23253.html</trackback:ping><description><![CDATA[<p><br>　 我开始并没太在意，但过了第三天还没见到珍妮，才感到不妙。一阵犹豫与绝望之后，我去找弗奈尔。找她是很自然的，因为我和珍妮当时都向她汇报工作。 </p>
<p>　 当我敲第二下门的时候，房门打开了。&#8220;啊，是你，&#8221;她说。&#8220;什么事？&#8221; </p>
<p>　 &#8220;正在找珍妮，好几天没见她了。&#8221;我解释道。 </p>
<p>　 弗奈尔扫了我一眼，说：&#8220;啊，是的。她最近分配了新的任务。&#8221; </p>
<p>　 &#8220;是吗，她在哪里呢？&#8221; </p>
<p>&#8220;在圆屋，那里和发掘现场都需要添加人手。她一旦去了就必须呆在那里了。去那儿是受限制的，因为门锁和地道情况不太好，进出那儿要穿特制的服装，真是痛苦。&#8221; </p>
<p>　 &#8220;是不是很多人都重新分配了任务？这里好像以后要搬空的样子。&#8221;我所知道的就是自从几周前的爆炸事件后，主圆屋那边显然还是一直在装修。表面上我们在外边的楼层里继续自己部门的工作，实际上与内部失去了联系，圆屋内大范围的通讯设施都有故障而在维修。 </p>
<p>　 &#8220;有一些，&#8221;她说着靠在了门口边上。&#8220;为什么？如果需要你这样的，要我给你介绍进去吗？&#8221; </p>
<p>　 &#8220;当然，&#8221;我脱口而出然而又奇怪自己反应这么快。我这么惦记珍妮吗？或者我急着再见到她？不管怎样，感觉自己有点奇怪。 </p>
<p>　 如果珍妮被转移到圆屋，我想如果有机会我也会去。忽然想起了很多年以前的一天，当时我学习处理另一种形式的转移。有意思的是，当时我也遇到了访问限制的问题。 </p>
<p><br>--------------------------------------------------------------------------------</p>
<p>　 正当温迪走过我的小卧室时，我正在大叫：&#8220;啊！我恨死非法访问了！&#8221; </p>
<p>　 挫折感太强烈了，我只想大喊大叫。然而我的声音终于烧焦般地减弱下去，最后变得和老鼠叫差不多了。 </p>
<p>　 这一切已经足够使温迪停了来看我了。&#8220;你还好吧，怎么回事，年轻人？&#8221;她的语调里带有一点爱尔兰口音。随着圣.帕特里克节的临近，她的一举一动越来越象爱尔兰人。 </p>
<p>　 &#8220;温迪，快帮我一个忙，&#8221;我叹了口气，&#8220;你用不着扮这副怪相，这里有一个怪人已经足够了。&#8221; </p>
<p>&#8220;喂，你还好吧？&#8221;她平静温和地说，&#8220;伙计，什么东西把你惹你这样？&#8221; </p>
<p>&#8220;啊呀，就是这些代码，我想可能和Guru有点关系。&#8221;我承认，&#8220;一开始我就被她吓得六神无主。我肯定她是从疯人院跑出来的。后来，我又有点喜欢她把我称作她的徒弟。是有点怪异，但也没什么大不了。但当我给朋友提起此事时，他们认为这是故意贬我。我再仔细回想一下，觉得他们说得没错。&#8221; </p>
<p>　 温迪笑了。&#8220;噢，&#8221;她说，&#8220;我明白了，事实上你没有从正确的着眼点去看待她。你应该去了解一下工作之外的她。&#8221;我试着想象她的家：架子上堆满了满是灰尘的电脑书和杂志；祭坛里烧着拜祭各位计算机科学家的香&#8230;&#8230;我的神情一定出卖了我的想法—温迪大笑起来，&#8220;她摆Guru的架子，主要是为了气气鲍勃，吓唬一下新手。我喜欢她这样子，否则她就活象一具僵尸了。&#8221; </p>
<p>　 &#8220;哦。&#8221;我并没有被说服。我不置可否，我觉得Guru的举动还是有点出格。，但我不想跟温迪争，于是叹了口气静下来回到那个问题上：&#8220;我矛盾了几个小时了。任务很简单—实际上就是重定向cout和cerr，把它们输出到文件中。&#8221; </p>
<p>　 &#8220;好，把你做的再跟我说一下。&#8221; </p>
<p>　 &#8220;我正在整合另一个组开发的库。这个库是以命令行（接口）的思路写出来的，所以所有的调试、诊断信息送到了cout与cerr。更糟的是我认为鲍勃在那个组，因为没有一个规则及原因说明何时信息送到cout何时送到cerr。实际上，有些信息分开送到两个流上！所以我要跟踪它们并把它们凑在一个log文件里。&#8221;</p>
<p>　 &#8220;总之，正如我说的，我在把它们整合到我们的图形用户界面（GUI）应用程序里。不幸的是，GUI直接扔掉了cout和cerr的输出。所以，我正做的就是将输出转向到一个文件里，实际上,简单吧，简单得我不知该怎么做，我对问题进行分析过滤后，写了下面的小程序。&#8221;我拉了把椅子让温迪在电脑旁坐下。 </p>
<p>#include &lt;iostream&gt;<br>#include &lt;fstream&gt;<br>int main()<br>{<br>&nbsp;&nbsp;&nbsp; std::ofstream logFile("out.txt");<br>&nbsp;&nbsp;&nbsp; std::cout = logFile;<br>&nbsp;&nbsp;&nbsp; std::cerr = logFile;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "This goes to cout\n";<br>&nbsp;&nbsp;&nbsp; std::cerr &lt;&lt; "This goes to cerr\n";<br>}</p>
<p>　 &#8220;程序在一个编译器上编译及运行没有问题，但换了个编译器就出了非法读取的错误。&#8221; </p>
<p>　 温迪看了看屏幕。&#8220;嗯&#8230;我不能确定你是不是可以那样重新分配数据流。&#8221; </p>
<p>　 &#8220;实际上，孩子，这样做显然极不自然明确地被神圣的标准所禁止。&#8221; </p>
<p>　 我们差点跳起来。不过怎么说，我得承认Guru总是神秘地在最恰当的时间出现。&#8220;所以，我这样做不行？&#8221;我叹道。 </p>
<p>　 &#8220;不，&#8221; Guru微笑道，&#8220;，实际上很简单，徒弟，你在大学里用什么编译器？&#8221; </p>
<p>　&#8220;噢，是一种很老的编译器，不支持标准的很多特性。&#8221; </p>
<p>　 Guru点点头。&#8220;我明白了。你的编译器应该在使用一种先知Langer 和 Kreft所谓的 经典输入输出流[1]。你肯定很熟悉标准输入输出流—尤其是流缓冲类。流缓冲不再是低级的实现上的细节；它现在是一个&#8230;完整的类。输入输出流的设计者，Jerry Schwarz 早就看到了流缓冲类将会成为一个极其有用的特性。然而它的价值和功能常常被忽略。&#8221; </p>
<p>　 &#8220;好吧。那我该怎么做呢？&#8221; </p>
<p>　 Guru拿起一枝干擦笔飞快地在白书写板上写下了如下的代码： </p>
<p>#include &lt;iostream&gt;<br>#include &lt;fstream&gt;<br>int main()<br>{<br>&nbsp;&nbsp;&nbsp; std::ofstream logFile("out.txt");<br>&nbsp;&nbsp;&nbsp; std::streambuf *outbuf = std::cout.rdbuf(logFile.rdbuf());<br>&nbsp;&nbsp;&nbsp; std::streambuf *errbuf = std::cerr.rdbuf(logFile.rdbuf());</p>
<p>&nbsp;&nbsp;&nbsp; // do the actual work of the program;<br>&nbsp;&nbsp;&nbsp; // GUI code and event loop would go here<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "This would normally go to cout but goes to the log file\n";<br>&nbsp;&nbsp;&nbsp; std::cerr &lt;&lt; "This would normally go to cerr but goes to the log file \n";<br>&nbsp;&nbsp;&nbsp; logFile &lt;&lt; "This goes to the log file\n";<br>&nbsp;&nbsp;&nbsp; // end of program body</p>
<p>&nbsp;&nbsp;&nbsp; // restore the buffers<br>&nbsp;&nbsp;&nbsp; std::cout.rdbuf(outbuf);<br>&nbsp;&nbsp;&nbsp; std::cerr.rdbuf(errbuf);<br>}</p>
<p>&#8220;rdbuf函数返回一个由基类basic_ios管理的流缓冲区的指针。重载版本允许你替换流缓冲区，返回值是原始的流缓冲区。解决方法很简单—用你的log文件的流缓冲区替换cout和cerr的流缓冲区。程序结束时，改回原来的流缓冲区。你可以看到，你仍能用logFile作为常规的输出文件。&#8221; </p>
<p>　&#8220;酷毙了！&#8221;终于看到了流缓冲区的威力。&#8220;嘿，如果我只想重定向一个流，比如cerr，我是不是可以简单地交换这两个缓冲区，这样就无须担心怎样恢复它们了，象下面那样 ：&#8221; </p>
<p>int main()<br>{<br>&nbsp;&nbsp;&nbsp; std::ofstream logFile("out.txt");<br>&nbsp;&nbsp;&nbsp; std::streambuf *saveBuf=cerr.rdbuf(logFile.rdbuf());<br>&nbsp;&nbsp;&nbsp; logFile.rdbuf(saveBuf);<br>&nbsp;&nbsp;&nbsp; // remainder of program...<br>}</p>
<p>　 &#8220;诶，并不这么简单，&#8221; Guru叹了口气 &#8220;这样的代码过不了编译关。basic_ios::rdbuf函数并不是虚拟函数。ofstream只提供了一个rdbuf函数，此函数没有参数，返回指向对象内部的文件缓冲流。也就是说std::ofstream::rdbuf(void) 隐藏了 std::ostream::rdbuf(std::streambuf *)。 </p>
<p>&#8220;你可以通过一个指向std::ostream的指针操纵你的log文件，如下：&#8221; </p>
<p>int main()<br>{<br>&nbsp;&nbsp;&nbsp; std::ofstream logFile("err.log");<br>&nbsp;&nbsp;&nbsp; std::ostream * baseManipulator = &amp;logFile;<br>&nbsp;&nbsp;&nbsp; baseManipulator-&gt;rdbuf(std::cerr.rdbuf(baseManipulator-&gt;rdbuf()));<br>}</p>
<p>　 这些代码能通过编译，但无法正常工作。神圣的标准并没有规定ofstream的具体实现方法。有可能某种编译器实现了ofstream的功能而忽略了ostream::rdbuf函数，直接操作文件缓冲流。标准没有要求ofstream::close调用ostream:: rdbuf来决定闭哪个缓冲。因为此函数总是返回指向ofstream对象内部的filebuf，ofstream总会关闭自己内部的filebuf对象，于是cerr就没有了有效的缓冲区。所以，你还是要恢复原来cerr的流缓冲区，main退出前加入如下： </p>
<p>std::cerr.rdbuf(baseManipulator-&gt;rdbuf());</p>
<p>&#8220;如果你想重定向cin的输入，你必须预防类似的事情，ifstream也是这样的。&#8221; </p>
<p>　 Guru转身准备离开了，又停住了说：&#8220;这周六我想请人到我家来。没什么特殊的，一个交际晚会。欢迎你和温迪，及你们各自的朋友前来。&#8221; </p>
<p>　 我开口正准备优雅的谢绝。&#8220;啊！&#8221; </p>
<p>&#8220;他的意思是他很乐意过去，&#8221;温迪笑得很甜。我的小腿被她踹了一下，痛得厉害，只能含泪无助地点点头。Guru微笑着飘然而去。 </p>
<p>　 我等Guru和疼痛都消失以后，对温迪大叫起来：&#8220;早晚修理你。&#8221; </p>
<p><br>--------------------------------------------------------------------------------</p>
<p>　 &#8220;好啊，&#8221;我对弗奈尔说，&#8220;别忘了通知我一声。&#8221; </p>
<p>&#8220;好的&#8221; </p>
<p>　 我就要走了，又问了一句：&#8220;诶，林格没却那儿吗，我的意思是她和珍妮是搭档。&#8221; </p>
<p>　 &#8220;不，&#8221;弗奈尔说。&#8220;我们这里需要她。&#8221; </p>
<p>　 &#8220;谢谢。明天见。&#8221; </p>
<p>在下一个值班时，我核查了一下我允许看的职责花名册。名单很长，上面的名字各式各样。象Jupiter（木星）这样的名字归联合国管啊，所以这次任务肯定是联合国发起的。各个地方的人员都有，主要来自美利坚联合体和亚洲联盟，因为它们投入的科研经费最多。没有谁的名字被打个记号，表明已被重新分配任务。但我能看出哪些名字已不在花名册的日常值勤中了，他们肯定去别的地方了。 </p>
<p>　 我慢慢看下去，看得很安静。 </p>
<p>　 我终于发现了怪事：没有一个亚洲人被再分配到发掘现场或是圆屋。 <br></p>
<img src ="http://www.cppblog.com/ornaking/aggbug/23253.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ornaking/" target="_blank">ornaking</a> 2007-04-30 17:11 <a href="http://www.cppblog.com/ornaking/archive/2007/04/30/23253.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++名家对话(08)</title><link>http://www.cppblog.com/ornaking/archive/2007/04/26/22894.html</link><dc:creator>ornaking</dc:creator><author>ornaking</author><pubDate>Thu, 26 Apr 2007 11:23:00 GMT</pubDate><guid>http://www.cppblog.com/ornaking/archive/2007/04/26/22894.html</guid><wfw:comment>http://www.cppblog.com/ornaking/comments/22894.html</wfw:comment><comments>http://www.cppblog.com/ornaking/archive/2007/04/26/22894.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ornaking/comments/commentRss/22894.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ornaking/services/trackbacks/22894.html</trackback:ping><description><![CDATA[<p><br>轰。 </p>
<p>巨响自远方传来。感觉上的难受更甚于听觉上的。挖掘队又开始工作了。 </p>
<p>&#8220;我希望&#8230;&#8230;&#8221;珍妮只开了个头就又没声了。 </p>
<p>&#8220;我知道。&#8221;我埋头于工作中。过了好一会儿，我加了句：&#8221;我也希望能给家里发封信。但通讯系统必须很快恢复工作才行。&#8221; </p>
<p>&#8220;它们能工作，&#8221; 珍妮说道。 </p>
<p>轰，又一声巨响。 </p>
<p>&#8220;是的，但因为圆屋的系统已被破坏，它被限定为只在紧急事件时使用。我们现在没有被授权。这是合理的。&#8221;我随口答道。 </p>
<p>又一阵的沉默。最后，珍妮低声道：&#8221;啊。好吧，他们是这么说的，而现在不是这种情况？&#8221; </p>
<p>&#8220;对。这只是暂时禁止。我也不喜欢这样，但你肯定不认为他们在&#8230;&#8230;&#8221;现在是我没声了。 </p>
<p>轰。 </p>
<p><br>--------------------------------------------------------------------------------</p>
<p>&#8220;轰&#8221;。 </p>
<p>这就是粘在我的显示器上的便笺上所写的内容。是的，还有跟着一个文件名我知道这个文件－我稍前加到project中的一个工具类。便笺是用Guru特有的笔法写的，但我搞不懂它是什么意思。Guru不在，我无法问她。 </p>
<p>就在此时，温迪，我隔间的程序员，进来并坐到她位置上。我探起头，期望于她能明白便笺的意思。她看起来很憔悴，头枕在手上。 &#8220;你看起来有个疯狂的周末，&#8221;我冒昧道。 </p>
<p>她无力地点点头。&#8221;周五我回到家时，一氧化碳探测器在报警－煤气在泄漏。我们不得不在一个朋友家渡过周末，直到它修好。我几乎没有合眼－我一直在想，如果没装探测器的话会发生什么[1] 。&#8221; </p>
<p>&#8220;噢，真是千钧一发，&#8221;我同情道，并在纸上记下要仔细检查我的探测器。&#8221;每个人都好吧？&#8221;温迪点点头。我认为她需要分散一下思维，所以就向她提起这个便笺并问她是什么意思。 </p>
<p>&#8220;等一下，&#8221;她答道，&#8221;Guru将纸条留在你显示器上？&#8221;我点点头。 &#8220;My friend，&#8221;她说，&#8221;听起来象你当了一回鲍勃。&#8221; </p>
<p>我惊呆了。&#8221;你在开玩笑吧！&#8221;我脸色发白。Bob was the worst programmer ever鲍勃一直是最差的程序员，我一有机会就攻击他和他差劲的编程风格。 </p>
<p>温迪摇着她的头，一脸的严肃。 &#8220;没有。一直以来他是唯一收到这种神秘纸条的人，如果你也收到一个，你肯定做了一次坏孩子。让我们看一下源文件。&#8221;她转向计算机，登录并调出有问题的文件。 </p>
<p>我搬过一把椅子。&#8221;在我看来，这儿一切都没问题，&#8221;我一边说一边从她肩后看过去。 </p>
<p>温迪轻微地动了一下－她比我预料得更专注。&#8221;粗看之下没问题，&#8221;温迪同意。&#8221;也许和以前的版本比对一下会找到一些线索。&#8221;我注视着她使用版本控制系统显示两个版本间的区别。我注意到－我仍然在试图掌握最基本的checkin/checkout指令，而此时她立刻就调用了一个版本比对命令。我还在试图归类温迪念出的命令。 </p>
<p>&#8220;有了，这而有些说法，但很不关键，&#8221;她指着屏幕说。&#8221;你更改了类的数据成员。&#8221; </p>
<p>我凑到屏幕前。 &#8220;是的，我小小地调整了其实现，我将标识对象位置的3个分开的double合并为一个double数组。它使得一些成员函数更具效率。但这些成员都是私有的，看到了吗？&#8221; </p>
<p>class Point<br>{<br>private:<br>&nbsp;&nbsp;&nbsp; double location[3]; // x, y, z coordinates.<br>public:<br>&nbsp;&nbsp;&nbsp; void setLocation(double x, double y, double z) { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; location[0]=x; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; location[1]=y;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; location[2]=z;<br>&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; void getLocation(double &amp;x, double&amp; y, double &amp;z) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; x=location[0]; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; y=location[1];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; z=location[2];<br>&nbsp;&nbsp;&nbsp; }<br>};</p>
<p>就在这时，我注意到比较器显示文件顶部的一个空行被修改了。&#8221;为什么显亮这一行？&#8221;我嚷起来。 </p>
<p>&#8220;也许是空格变化，&#8221;温迪猜测。&#8221;那么，让我们设置为忽略空格。&#8221;在一些击键动作后，于是&#8230;&#8230;这一行仍然处于显亮状态。我将光标定位到这一行，并按下&#8221;End&#8221;键－骇人听闻的事情出现了。缩进到最右边的出乎常理地，是这样一条语句： </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #define private public</p>
<p>温迪和我互相看了一下。. &#8220;鲍勃！&#8221;我们齐声道。 </p>
<p>鲍勃，就在此时,正向我们走来。Guru不是唯一&#8221;说曹操就曹操到&#8221;的人。&#8221;嗨，新来的，&#8221;他说道，&#8221;我正巧路过，就来看一下你是否已经改好了问题。&#8221; </p>
<p>我闭上眼并数十。慢慢地。然后，又数回零。我举起便笺：&#8221;你说的是这个问题？&#8221;我轻快地问道。 </p>
<p>&#8220;嗨。是的，就是这个。她莫名其妙地将这个留在我的显示器上。当你将x、y和z改到数组中时，它搞砸了我大部分代码，所以我把它传给了你。改掉它，从现在起再别乱搞了，可以吧，新来的。&#8221; </p>
<p>&#8220;鲍勃，这个#define语句是什么意思？&#8221;我指着那行讨厌的代码。 </p>
<p>&#8220;哦这个，&#8221;鲍勃呵呵笑了。 &#8220;很酷的，不是吗？我的代码需要直接访问相关数据。高效，, y'know你知道的。起先，我只在我自己的代码中使用这个#define语句，但我一直这么使用，所以很自然地就把它加到你的头文件中了。我要处理非常多的数据运算，所以不能承受你提供的访问函数的开销。但当你用数组取代了单独的double时，我的代码&#8216;轰&#8217;了。&#8221; </p>
<p>&#8220;鲍勃，实际上。&#8221;好象事先约好的一样，我们听到了Guru的声音。我们都跳了一下。她用手指推了一下鼻子上的眼镜。&#8221;而且，你对编程原则的曲解&#8221;她指责道，&#8221;是我留便笺在你显示器上的原因。你试图搅乱访问控制的行为打破了类的封装。你在代码中将private改为public的行为违背了One Definition Rule。你必须将你的代码改为使用访问函数。&#8221; </p>
<p>&#8220;我已经说过了，我不能承受访问函数的开销，亲爱的,&#8221;鲍勃辩解道。&#8221;我必须－&#8221; </p>
<p>&#8220;不要说了！&#8221; Guru打断了他&#8221;访问函数是内联的，不会造成开销。&#8221;鲍勃试图反驳，但Guru阻止了他。&#8221;那么，为了避免所有的流言[2]，我们用测试来证明访问函数的实际代价。在此期间，清理掉那些不象样的代码。然后研读并思考1 Meyers 20[3]。&#8221; </p>
<p>鲍勃嘟囔着走向他自己的房间。Guru静静地站着，直到他已经走了，然后领着我回到我自己的房间。 </p>
<p>&#8220;徒弟，&#8221;她轻柔地说道，&#8221;请写一段测试代码来计算直接访问的开销，和inline的访问函数以及非inline的访问函数进行对比。要确保非inline的访问函数处理的位于其它的编译单元中。While you are at it, an inline operator [] would be useful.而inline的operator []会有帮助。&#8221; </p>
<p>我想了一下。&#8221;你是这个意思吗？&#8221;我写出： </p>
<p>class Point<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; inline double operator[] (int index)&nbsp; const {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return location[index];<br>&nbsp;&nbsp;&nbsp;&nbsp; }<br>};</p>
<p>&#8220;很好，徒弟。你记住了正确使用const的法则。但现在，这么写它将不只是一个访问函数，还有个很好的mutator（变异）函数。你能从这个函数得到什么额外的好处？&#8221; </p>
<p>我想了一会儿。&#8221;可以在下标上进行越界检查。&#8221;我修改了一下： </p>
<p>class Point<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; inline double &amp;operator[] (int index) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assert(index &gt;=0 &amp;&amp; index &lt;= 2);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return location[index];<br>&nbsp;&nbsp;&nbsp;&nbsp; }<br>};</p>
<p>&#8220;嗨，漂亮吧！&#8221;这个，我喜欢。&#8221;这个越界检查在release版本中没有任何性能开销。&#8221; </p>
<p>Guru点点头。&#8221;并且，&#8221;她又说道，&#8221;使用一个优化能力很强的编译器时，能生成和直接访问成员同样高效的代码，而又兼具着封装及其安全性。&#8221; </p>
<p>&#8220;所以，&#8221;我思索着，&#8221;实在没有理由存在公有数据成员，是吧？&#8221; </p>
<p>Guru在回答前停了一下。 &#8220;在绝大多数情况下，没有理由。然而，如果一个类仅仅是一个便利性的数据聚合体，而不是对象模型－也就是说，C风格的struct，只有数据而没有额外行为－那么将所有数据置为公有是合理的 [4]。&#8221; </p>
<p>&#8220;你可以通过访问函数和mutator函数将私有成员暴露出去－但只在它真的有必要时。多余的Get和Set函数－包括此处的operator []－表明你没有认真考虑封装。当测试表明访问函数真的存在运行期开销时，你可以通过将它们实现为inline来提高速度。当然，所得的提高因编译器而异－所以需要测试。&#8221; </p>
<p>&#8220;我明白了，老板，&#8221;我高兴地回答。她笑了，拢着手静静地走开了。我坐下来写好了测试代码。其结果,至少就我所使用的编译器而言，非常有趣[5] ： </p>
<p>&nbsp;Approximate run-time in milliseconds&nbsp; <br>Implementation&nbsp; Optimizations off&nbsp; Optimizations on&nbsp; <br>1: direct access&nbsp; 1061&nbsp; 251&nbsp; <br>2: inline accessor function&nbsp; 1673&nbsp; 240&nbsp; <br>3: out-of-line accessor function&nbsp; 1662&nbsp; 1432&nbsp; </p>
<p>--------------------------------------------------------------------------------</p>
<p>轰。 </p>
<p>&#8220;我不喜欢这样，&#8221;珍妮重复道。&#8221;他们说圆屋因为一起意外的内部爆炸而被破坏，所以我们不能去那儿，但那儿的工作还在继续，有着更高许可的人被允许进入并且没有出来过。他们说远距通讯设施被破坏，并限定只供紧急状态下使用，但我能看到的唯一结果是我们不能和近木星空间的其他任何人联系。&#8221; </p>
<p>&#8220;是吗？&#8221; </p>
<p>&#8220;我不喜欢这样。&#8221; </p>
<p>轰。 </p>
<p>我们又有一段时间没说话，最终远处的轰鸣也停止了，但得到的寂静比烦躁的噪音更难受。left.过了一会儿，珍妮皱起眉头，站起来离开了。 </p>
<p><br>--------------------------------------------------------------------------------</p>
<p><br>&nbsp;</p>
<img src ="http://www.cppblog.com/ornaking/aggbug/22894.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ornaking/" target="_blank">ornaking</a> 2007-04-26 19:23 <a href="http://www.cppblog.com/ornaking/archive/2007/04/26/22894.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++名家对话07</title><link>http://www.cppblog.com/ornaking/archive/2007/04/01/21059.html</link><dc:creator>ornaking</dc:creator><author>ornaking</author><pubDate>Sun, 01 Apr 2007 13:49:00 GMT</pubDate><guid>http://www.cppblog.com/ornaking/archive/2007/04/01/21059.html</guid><wfw:comment>http://www.cppblog.com/ornaking/comments/21059.html</wfw:comment><comments>http://www.cppblog.com/ornaking/archive/2007/04/01/21059.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ornaking/comments/commentRss/21059.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ornaking/services/trackbacks/21059.html</trackback:ping><description><![CDATA[<p>-------------------------------------------------------------------------------</p>
<p>&#8220;下周，队伍就要下去了，我想我们会在一周后到那儿。&#8221;珍妮总结道，&#8220;他们仍在水晶石塔上方挖掘，但没有什么新发现。进展很缓慢，我们不打算进行这样的探查。这超出了我们的探索和采矿的命令。弗兰奈尔仍然认为这是个玩笑。&#8221; </p>
<p>我笑道：&#8220;弗兰奈尔总是留有余地，要从他那儿掏出点什么。颜色不对，地点也不对，真是——&#8221; </p>
<p>&#8220;是啊，几个月前我们就知道它深处冰层之下。&#8221; </p>
<p>&#8220;形状也不对，如果有人真想开玩笑，完全可以做得比这个高明些。不管怎样，没有一个开玩笑的人会搞得这么复杂。&#8221; </p>
<p>&#8220;我已经听了一个好一点的消息，&#8221;珍妮打断道：&#8220;奥布里认为这是真的，但是他认为这是阴谋。如果是2001年，年代好象太近了。&#8221;她用用她那夸张的特有的口音模仿那些年轻职员。　 </p>
<p>&#8220;哦，奥布里发现政府到处在掩饰些什么。记不记小时候，当魔幻跳弹被藏起来后我们暴跳如雷的样子？&#8221;我停顿了一下，意味深长地笑了一下，&#8220;谈到庞然大物，我有没有跟你说起过我在新年后的奇怪发现，那是在真正的2001年......？没有？那好，这事发生在我第一份工作期间......&#8221; </p>
<p><br>--------------------------------------------------------------------------------</p>
<p>那是一月二日，一个星期二，我发现鲍勃的大块头代码。我们刚过完假期，每个人都兴高采烈，肚子吃得圆圆的。不幸的是，交付期限可不等人。慢慢地，我们又回到了紧张的工作状态。我也很高兴我已经设法熬过我的工作试用期了，现在我是真正的雇员了。尽管我有时仍然怀疑这是不是完全的好事，因为现在的工作使我跟Guru捆在一起。她很能干，确实如此，但又有点怪异。 </p>
<p>当温迪打断我的时候，已经快吃午饭了。&#8220;哎，&#8221;她说道。 </p>
<p>我抬起头：&#8220;什么事？&#8221;我正在执行一个调试任务，想找出我代码中的一个错误，正准备休息。跟几十次单步跟踪同一个函数相比，干啥事都要强上一筹。 </p>
<p>温迪道：&#8220;鲍勃这个星期还在外边，我需要快速修复你上个月让他接手的功能模块；我在第510行发现缓冲器溢出错误,你能在今天下午找到它并修复吗？&#8221; </p>
<p>&#8220;当然。&#8221; 她给了我一个文件名，我把它调入了我的编辑器。 </p>
<p>一个半小时后，我必须承认我的错误：调试程序里真的有一些比我单步跟踪代码更坏的事情。我最终放弃了，朝着温迪的房间走去。&#8220;对不起，&#8221;我沮丧的说。 </p>
<p>温迪吃了一惊，我估计她没有听到我来到她身后。我挤出一点笑容....这通常Guru对我们的动作。&#8220;对不起，你说什么？&#8221;她问道。 </p>
<p>&#8220;你希望能早一点修复代码，但我很对不起，温迪，我恐怕我办不到。&#8221; </p>
<p>&#8220;出了什么问题？&#8221;　 </p>
<p>&#8220;我想你已经知道了我刚才所出的问题。&#8221;　 </p>
<p>&#8220;你在说什么？&#8221;　 </p>
<p>我叹了口气，不再捉迷藏：&#8220;我无法很快修复它。在我把这个模块交给鲍勃后你知不知道鲍勃是怎么干的？我发誓他的做法跟我的毫无相似之处。它的原来面目、它的目的对我来说完全是个谜。我必须要半天时间去研究他在这上面做了些什么。&#8221;我靠近她，在她的屏幕上拉出文件，来到函数开始的地方，然后按住滚屏键，几乎2000行以后，我们在文件的末尾找到了代表函数结束的}号。&#8220;这个函数现在的长度是1908行。大致是一个1200行的if语句块，接下来是700行的else语句块。我想剩余的大部分都可用源代码优化器来修正。&#8221; </p>
<p>温迪难以置信地摇摇头，但最终又点了点头：&#8220;我以前看他这样做过，但我真的想阻止他任意地使用标号和长长的代码行，尤其是随意的垂直空格。&#8221; </p>
<p>&#8220;至少有一个地方，你必须翻遍整屏的空行才能看到下一条代码行。并且你有没有注意到有时他在进入for或while语句块后用goto跳出到标号处? Guru从来不提倡这么做 . . . 她不是要实行代码的标准化吗？&#8221; </p>
<p>&#8220;事实上我们是这样做的，我的孩子，&#8221;一个感叹的声音从我们身后传来，我们都跳了起来。Guru忧愁地继续道，&#8220;Bob已经被警告过了。他应该再被警告一次，而这次他应该被扣工资了。但是 . . .&#8221;她思考了一下。　 </p>
<p>温迪和我都不想立即说话。沉默镇定了。最终,我静静地催促着&#8220;但是，什么？&#8221;　 </p>
<p>Guru慎重地眨眼睛了：&#8220;文件里最长的一行有多长？&#8221;　 </p>
<p>温迪快速的检查了一遍：&#8220;确切的说最长的有848个字符。这是最长的。&#8221; </p>
<p>&#8220;最短的有多长？&#8221; </p>
<p>这用了温迪更长时间：&#8220;他最短的没有空白的代码行是212个字符。这么长的有好几行。&#8221; </p>
<p>Guru好象了做一些快的心算，并且再一次叹息：&#8220;我明白了，也许他在做一个声明。&#8221;　 </p>
<p>我想了一会儿说：&#8220;Oh，我了解了：1～4～9。这是一行的长度和整个函数大小的比率。&#8221;　 </p>
<p>温迪叹息。　 </p>
<p>Guru把她的头发推到她的耳朵后边，调整了她的眼镜。&#8220;避免了整块集成代码，&#8221;她说。&#8220;Bob是一个结构化的程序员，但是，他应该好好地了解功能分解的存在价值。这个函数很明显可以分成两部分。但是，甚至那些部分都会太复杂，还应该进一步被分解。温迪,教这年轻人：好的代码分解有什么好处？&#8221; </p>
<p>&#8220;清楚，&#8221;温迪说，敲打这每个手指的关节。&#8220;复用性。封装。可维护性 . . .&#8221;　 </p>
<p>正在此时，就在这个不幸的瞬间，我们听到前门打开了，从外面传来口哨声，接着门又关上了，然后鲍勃走了进来。 </p>
<p>&#8220;嗨，花花公子，&#8221;鲍勃说道，他脱下他的手套并在地毯上敲打靴子上的雪。他心不在焉地抓了抓脑门，&#8220;什么事？&#8221;　 </p>
<p>&#8220;我以为你整个星期都不来了呢。&#8221;温迪抱怨道。 </p>
<p>&#8220;对，确实如此，小乖乖。我过来拿一些下载在这里的文件...并上上网....嗯，哈，当然，这些并不重要。&#8221;他斜视了一下温迪的显示器，&#8220;那是我的代码。&#8221;　 </p>
<p>&#8220;那是我的代码。在你使它变成玩笑前，它曾是一段不错的代码，&#8221;我激动的说。鲍勃比我有更多年的经验，但是我的试用期已结束了，我很生气，我猜测我本可以还有一点理智，我应该有相当美好的最后几个夜晚。我觉得好象是在冒着危险。　 </p>
<p>鲍勃耸耸肩：&#8220;嗨，看。我明白你对这很心烦。&#8221;　 </p>
<p>&#8220;心烦？鲍勃，&#8221;我反驳道，&#8220;你已经把我的代码变得很滑稽！&#8221;　 </p>
<p>&#8220;这只能归因于人的错误，&#8221;Guru平静地表示赞同。　 </p>
<p>&#8220;嗨，&#8221;Bob自卫地说，&#8220;我真心希望你能平静下来。吃一粒镇定药并且仔细思考一下这件事。&#8221;　 </p>
<p>&#8220;鲍勃，&#8221;Guru说道，&#8220;滚。&#8221;　 </p>
<p>&#8220;嗨，我并被要求...&#8221;这时鲍勃才明白过来，并冷静下来，&#8220;嗨，看。很好。我知道最近我的某些决定不是很好，但我向你绝对保证我的工作会恢复正常的...&#8221; </p>
<p>&#8220;鲍勃，这谈话已经达不到目的了。再见，去过你的假期吧。我们会在周一私下再讨论这件事。　 </p>
<p>鲍勃抱怨着，但是好像仔细的考虑了一下就离开了。Guru也是。两个人都从听力所及的范围里消失了。温迪和我都咯咯的笑了起来。当我们恢复过来后，温迪通过源代码优化器执行了文件，而我把当天剩余的时间都用于重写鲍勃的代码。我发现作为结果产生的更小的函数应该被重用，现在它们的功能很好地被分离开来，比集成在一个大块里好多了。更小的代码块更容易理解，很快我就发现了温迪的问题，并把它解决了。我给温迪的问题添加了一个单元测试，重新执行了整个单元测试，确保一切顺利，并重新检查了整个模块。 </p>
<p><br>--------------------------------------------------------------------------------</p>
<p>&#8220;你肯定这是你第一次在工作中发火，&#8221;珍妮摇了摇头。　 </p>
<p>在我正要回答时，休息处门开了，布拉森科沃斯基走了进来。 他看也不看的朝休息室另一边走去，对他来说有点不同寻常。　 </p>
<p>&#8220;嗨，&#8221;我叫道，&#8220;你在那里都不打招呼？&#8221;　 </p>
<p>布拉森拉沃斯基转了过来：&#8220;什么？哦，对不起，你刚才听到消息了吗？&#8221;　 </p>
<p>珍妮皱眉了：&#8220;什么消息？从发掘现场那里来的吗？&#8221;　 </p>
<p>&#8220;对，看上去那个方形尖塔，&#8221;布拉森拉沃斯基平静地说，&#8220;不仅仅是个石塔，它好象是某个建筑物的顶部。&#8221;　 </p>
<p><br>--------------------------------------------------------------------------------</p>
<p>作者注：相不相信随你，这的确是作者几年以前遭遇的程序员写代码中的真实的例子。 <br></p>
<img src ="http://www.cppblog.com/ornaking/aggbug/21059.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ornaking/" target="_blank">ornaking</a> 2007-04-01 21:49 <a href="http://www.cppblog.com/ornaking/archive/2007/04/01/21059.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++名家对话06</title><link>http://www.cppblog.com/ornaking/archive/2007/03/30/20977.html</link><dc:creator>ornaking</dc:creator><author>ornaking</author><pubDate>Fri, 30 Mar 2007 13:40:00 GMT</pubDate><guid>http://www.cppblog.com/ornaking/archive/2007/03/30/20977.html</guid><wfw:comment>http://www.cppblog.com/ornaking/comments/20977.html</wfw:comment><comments>http://www.cppblog.com/ornaking/archive/2007/03/30/20977.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ornaking/comments/commentRss/20977.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ornaking/services/trackbacks/20977.html</trackback:ping><description><![CDATA[
		<p>(好长时间没有看什么书了, 现在难得开始静下心来慢慢做毕业设计了, 也算是有时间了吧!)</p>
		<p>　 我又拧了一下扳手 ，但毫无用处，我应该试着用锤子猛敲它 ，但那看起来不是个好主意。“它一动不动”，我说道，“剩余的空间足够再放一个模块，但原先那些零件固定得很紧，动不了。” </p>
		<p>　 机器只允许一个人操作，珍妮只能旁观，她忍不住抱怨：“，唉，真是的，往扳手上添个零件就这么困难么？” </p>
		<p>　 “如果零件没有正确组合在一起的话，就可以”，我叹道，“那些制造的人只需要考虑东西能用就可以了，里面的东西都紧紧的合在一起了，行，我们可以添加零件，但是首先，我们不得不把它先拆开来，然后再重装好。” </p>
		<p>“欢迎到木卫二, 自身体重牵引系统中心，回家后我们就拥有这项技术了，可以自己试试了。” </p>
		<p>我摸了摸额头，“你知道”，我沉思一会说到，“这让我想起...”,珍妮笑了，这表明我们可以休息一下喝杯咖啡了，我开始告诉她另一个关于我第一份工作的故事。 </p>
		<p>
				<br />--------------------------------------------------------------------------------</p>
		<p>　 我正在为一个设计方案编写代码，该方案是住我隔壁的程序员温迪设计的。她为项目设计一个抽象类，而我的工作就是设计一个实体类继承她的抽象类。 </p>
		<p>　 就像往常一样，我通过公共接口来研究一个新的类： </p>
		<p>class Mountie<br />{<br />public:<br />    void read( std::istream &amp; );<br />    void write( std::ostream &amp; ) const;</p>
		<p>    virtual ~Mountie();</p>
		<p>};</p>
		<p>　 毫不惊奇—析构函数设计成虚拟函数，暗示这个类将作为基类使用；但用于读与写的成员函数却是非虚拟的，这时我想要看看其他代码： </p>
		<p>　 所以我把目光移到类的保护接口上： </p>
		<p>protected:<br />virtual void do_read( std::istream &amp; );<br />virtual void do_write( std::ostream &amp; ) const;</p>
		<p>　 很快，我意识到温迪使用了模板方法模式（Template Method pattern)：公共、非虚拟成员函数显然将调用受保护的虚拟函数[1]。我对很快理解了这点感到洋洋自得，我做好了应付任何事情的准备。可当我看到私有接口时，我的这股得意劲消失了。 </p>
		<p>private:<br />    virtual std::string classID() const = 0;</p>
		<p>　 我停止了自得，一个纯虚拟私有函数？它怎么可能工作呢？我百思不得其解。 </p>
		<p>　 “嗯，温迪”我说，“你的mountie类无法工作。它有一个私有的虚拟函数。” </p>
		<p>　 “你试过吗？”温迪问道，头也没从她的键盘上抬一下。 </p>
		<p>　 “噢，好吧，没有”,我承认，“但我的继承类无法覆盖classID这个函数” </p>
		<p>　 “你如此肯定?”Guru安静的声音把我们都吓了一跳。“和你在一起总是什么事都干不成。难道这几个月来你什么都没学到，还是个菜鸟？你可是我的徒弟啊！ </p>
		<p>　 尽管Guru的语气很平静，但这几句咄咄逼人的话仍使我感到几分惶恐。 </p>
		<p>　 “我的孩子“，她继续说道，“你忘了存取权限和虚拟性是互相独立的，决定一个函数是静态绑定还是动态绑定取决于这个函数最后是如何被调用的。你需要好好读读神圣标准第三章4节第一行，和第五章2.2节第一行。 </p>
		<p>　 是时候展现我的水平了，我开口说：“噢，是的”，我决定使出另外一招---扯开话题。“我只是不理解为什么它是私有的。这看起来好像对我没什么意义。” </p>
		<p>　 “仔细考虑一下这种情况，徒弟。你创建了一个类，再仔细考虑一下它的设计以后，你觉得这个类需要一个不能被任何其他类调用的成员函数，甚至是它的继承类，你会怎么做呢？” </p>
		<p>　“当然是申明为私有了”，我回答道。Guru看着我，她的眉毛皱了起来。我的大脑开始飞速的转了起来，拼命的想私有函数和虚拟函数之间有什么联系。 </p>
		<p>　 温迪说：“你检查过mountie类的实现没有，特别是write函数？” </p>
		<p>　 我很快的转到我的键盘上，很高兴逃离Guru不松懈的注视。我很快就看到了它： </p>
		<p>void Mountie::write(std::ostream &amp;Dudley) const<br />{<br />    Dudley &lt;&lt; classID() &lt;&lt; std::endl;</p>
		<p>    do_write(Dudley);<br />}</p>
		<p>　我应该告诉温迪我童年花了太多的时间看动画片了。“我现在明白了”，我说道“函数classID是一个实现细节，用来表示正在保存的类的具体类型。继承类必须重载这个函数，但既然它是个实现细节，那就让它为私有。” </p>
		<p>　 “不错，小伙子”Guru点点头“现在解释给我听听为什么do_write和do_read不是私有的。” </p>
		<p>　 这让我想了好一会儿，突然灵机一动：“因为继承类必须调用它父类这些函数的实现以使父类有机会读写继承类的数据。” </p>
		<p>　 “很好，徒弟，”Guru几乎微笑了,“但为什么不让它们为公有?” </p>
		<p>　 “因为,”我受到鼓舞继续说道,“它们必须以一种受控制的方式被调用，特别是do_write函数，对象类型不得不首先写到流中，使得当对象被读取时，对象的创建者得以知晓对象的类型以便从流中装载并创建对象。如果把这些函数设为公有，会导致混乱。 </p>
		<p>　 “非常聪明，徒弟。”Guru暂停了一会，“就像口语一样，c++语言除了语法和规则外还有很多东西需要了解，你必须要知道这些东西。 </p>
		<p>　 “对，Coplien的书是我下一本要读的书。” </p>
		<p>Guru抬起手：“安静点，孩子。我不是说大师Coplien。我是说用词习惯的语言感觉---特定结构下的隐含意思。举例来说，你知道，一个虚拟析构函数将会告诉你我将会作为多态基类，你可以从我这里按你所需继承新类，但是一个非虚拟析构函数则告诉你我不会作为多态基类，请不要从我继承，我祈祷。” </p>
		<p>　 　 “类似的，虚拟函数的存取权限表达了初始化的通常意义。一个保护的虚拟成员告诉你一些东西应该或可能甚至必须依赖于这个函数的实现。一个私有的虚拟函数表明某些东西可能或者不可能会重载我，这取决于他们的选择。然而他们可能不会调用我的实现 。” </p>
		<p>　 当我领会到这点，我点点头 ：“那么公共虚拟函数呢？” </p>
		<p>　 “尽可能避免这样做，最好使用模板方法，考虑这种情况”，她拾起干擦笔，开始写了起来，字迹很细： </p>
		<p>class HardToExtend<br />{<br />public:<br />    virtual void f();<br />};<br />void HardToExtend::f()<br />{<br />    // 执行专有的动作<br />}</p>
		<p>　 “假设你发布了这个类，但不久，你的需求发生了变化”，她继续说道，“在这个类的第二个版本里，你发现在函数f()中需要实现模板方法(Template Method )。而这几乎不可能实现，你知道这是为什么？ </p>
		<p>　 “啊，是的，好吧...我认为...不，我不知道。“ </p>
		<p>　 “有两个可能的途径让这个类使用模板方法。第一个途径是把f()的实现代码移到一个新的函数然后让f()成为非虚拟的，就像这样： </p>
		<p>class HardToExtend<br />{<br />    // 可以保护<br />    virtual void do_f();<br />public:<br />    void f();<br />};<br />void HardToExtend::f()<br />{<br />    // 前处理<br />    do_f();<br />    // 后处理<br />}<br />void HardToExtend::do_f()<br />{<br />    // 执行专有的动作<br />}</p>
		<p>　 “然而，HardToExtend’的继承类希望能覆盖f(),而不是do_f().你现在必须改变所有从HardToExtend继承而来的类。只要你漏掉了一个，这个类将试图覆盖一个非虚拟函数，这将会导致，用大师Meyers的话来说，在你的类继承体系中有不可预期的行为。 </p>
		<p>　 “另一个解决之道是引进非虚拟函数，然后把f()移到私有部分，像这样：” </p>
		<p>class HardToExtend<br />{<br />// possibly protected<br />virtual void f();<br />public:<br />void call_f();<br />};</p>
		<p>　 “这将会给类的使用者无休止的烦恼。所有HardToExtend的使用者都会试图调用f(),而不是call_f()。 HardToExtend的客户代码根本就无法编译。此外，它的继承类将很可能把f()放到公共接口里，然后使用这些继承类的客户代码会直接地，而不是通过HardToExtend的指针和引用，对你想保护的函数进行存取。 </p>
		<p>　 “虚拟函数应该和数据成员一样对待----让他们成为私有的，除非设计需求表明应该有较少的限制。提升它们到更高存取级别比把它们降到更私有的级别更容易些。” </p>
		<p>
				<br />--------------------------------------------------------------------------------</p>
		<p>　 “这些事是什么时候发生的呢？”珍妮问道。 </p>
		<p>　 “就在新千年来临之前---真正的来临，也就是说，我想是在2000年末。我们那时候即将庆贺新年，我们那时在展望2001年，因为所有的文化，你知道.. .” </p>
		<p>　 很奇怪，这场谈话后没多久我们听说在木卫二地表下面发现了一个方尖塔。 </p>
		<p>
				<br />--------------------------------------------------------------------------------<br /> <br /></p>
<img src ="http://www.cppblog.com/ornaking/aggbug/20977.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ornaking/" target="_blank">ornaking</a> 2007-03-30 21:40 <a href="http://www.cppblog.com/ornaking/archive/2007/03/30/20977.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++名家对话 05</title><link>http://www.cppblog.com/ornaking/archive/2006/12/01/15866.html</link><dc:creator>ornaking</dc:creator><author>ornaking</author><pubDate>Fri, 01 Dec 2006 14:06:00 GMT</pubDate><guid>http://www.cppblog.com/ornaking/archive/2006/12/01/15866.html</guid><wfw:comment>http://www.cppblog.com/ornaking/comments/15866.html</wfw:comment><comments>http://www.cppblog.com/ornaking/archive/2006/12/01/15866.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ornaking/comments/commentRss/15866.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ornaking/services/trackbacks/15866.html</trackback:ping><description><![CDATA[
		<p>通过任何其他名字 <br /></p>
		<p>--------------------------------------------------------------------------------</p>
		<p>悬在几乎一百公里以外的天空，那个小小的行星占据了观察孔视野相当大的部分。它的表面看起来好像玻璃碎裂后又用冰冻的胶水修补过。许多冻结的河流蜿蜒在十字形的低矮山脉之间。荒凉的木卫二是那样的美轮美奂。　 </p>
		<p>扬声器传出“上船，第三和第七区”，然后又恢复沉默。　 </p>
		<p>珍妮和我在观察口边上和一群专家挤作一团，我们中的大多数看起来像是瞠目结舌的年轻宇航员。可我们不在乎。当然，在飞船中的任何地方都可以看到更好的景色，但当看到真实、原原本本的光直接从月球反射进你的眼睛时，总能有一丝特殊的感觉。　 </p>
		<p>“真是个好地方，” 珍妮自告奋勇的说。“他们将消息封锁得紧紧的，现在也还这样。” </p>
		<p>“紧紧的？他们应该告诉我们真正的目的地。” </p>
		<p>“不会。我相信我们会很快找出他们在冰层底下发现了什么。”　 </p>
		<p>“嗯？嗯。”我回答，“仅仅来到这里，我就很高兴。当我以前看到这里的图片，还以为是处理过的幻境，没想到真的这么美——不，比图片更美。我们真的来到这里了。这使我想起自己的第一份工作。”　 </p>
		<p>她开玩笑地哼了一声。“任何东西都会使你想起第一份工作。” </p>
		<p>“我还年轻，所以容易受到感染。无论如何，刚才关于这次旅行的谈论又使我想起了它。” </p>
		<p>“啊。”她停顿了一下。“你到底想说什么？” </p>
		<p>我笑了，接下去解释……　 </p>
		<p>
				<br />--------------------------------------------------------------------------------</p>
		<p>我曾经为一个设计问题苦苦挣扎。我真的希望自己解决，因为见习期即将结束，我希望证明自己。　 </p>
		<p>你记得毕业后的第一份工作，对吧？正确地处理事情是多么的重要。我一直记得新进公司的同事们，因为无法与Guru打好交道而没有通过见习。别误解我的意思，她是一个优秀的程序员，只是有一点儿……古怪。现在她是我的良师益友，我非常希望在她面前证明自己。　 </p>
		<p>我需要在类体系中加入一个新的虚函数，但其他团队维护着类体系并且不允许别人改动。这个类体系看起来像是这样：　 </p>
		<p>class Personnel<br />{<br />public:<br />  virtual void Pay ( /*...*/ ) = 0;<br />  virtual void Promote( /*...*/ ) = 0;<br />  virtual void Accept ( PersonnelV&amp; ) = 0;<br />  // … 其他函数 …<br />};<br />class Officer : public Personnel {{ /* 改写虚拟函数 */ };<br />class Captain : public Officer { /* 改写虚拟函数 */ };<br />class First : public Officer { /* 改写虚拟函数 */ };</p>
		<p>我需要一个函数在对象是Captain和First军官时拥有不同的行为。虚拟函数恰好合适，在Personnel或Officer中声明，在Captain和First中改写。　 </p>
		<p>麻烦是：我不能加入新的函数。我知道仅仅加入使用运行时类型信息的类型检查就可以解决问题，像这样：　 </p>
		<p>void f( Officer &amp;o )<br />{<br />  if( dynamic_cast&lt;Captain*&gt;(&amp;o) )<br />    /* do one thing *//* 某些操作 */<br />  else if( dynamic_cast&lt;First*&gt;(&amp;o) )<br />    /* do another thing *//* 某些操作 */<br />}<br />int main()<br />{<br />  Captain k;<br />  First s;<br />  f( k );<br />  f( s );<br />}</p>
		<p>但我知道这种类型检查是不符合公司的编程规范的。“我不喜欢这样，”我对自己，也对别人这样说：“但这一次，我觉得有充足的理由让他们更改这个规范。很显然，没别的方法可以解决问题啊。”　 </p>
		<p>“所有的问题都可以通过多加一个间接层来解决。” </p>
		<p>我吓了一跳，Guru在我身后说话呢！“什么？”我转向她问。 </p>
		<p>“所有的问——” </p>
		<p>“哦，是的。”我禁不住打断她。“我听见了你所说的，我只是不知道您从哪儿来的。”老天！我想这是一种掩饰，不过部门里的大半同事都会这么做的。　 </p>
		<p>“啊，不过你会的。”Guru把身体探向我说：“你会知道的，我年轻的徒弟。”她专注地盯了我一会儿，然后直起身子。“我的孩子，C的信徒经常使用基于对象类型的分支语句来引导程序流。考虑这个比喻。”她拿起一支干擦笔： </p>
		<p>/* 一个典型的C程序 */<br />void f(struct someStruct *s)<br />{<br />  switch(s-&gt;type) {<br />  case APPLE:<br />    /* 进行某些操作 */<br />    break;<br />  case ORANGE:<br />    /* 进行某些其他操作 */<br />    break;<br />  /* 等等 */<br />  }<br />}</p>
		<p>“当这些信徒向先知Stroustrup学习时，”她继续说，“也就是说学习C++时，他们必须学习如何设计好的类体系。” </p>
		<p>“是的，”我再次打断，渴望表现出自己确实懂一些东西，“他们应该创建一个以Fruit为基类的类体系，然后派生Apple和Orange。这不错吧？派生类中应该使用虚拟函数。” </p>
		<p>“很好，我的孩子。C++的信徒经常借用这个比喻指出switch语句烦琐，容易滋生错误，劝说C的执迷者改投C++的怀抱。。然而从另一种角度看待这个比喻：通过引进一个虚拟函数，你就加入了一个间接层。”她放下笔，“你所需要的是一个新的虚拟函数。” </p>
		<p>“啊哈，很对。我知道，”我炫耀地总结道：“而且我现在无法这样做。” </p>
		<p>她点点头。“因为你不能修改类体系。是的。” </p>
		<p>这使我迟疑了一下，但仅仅是一瞬间。“哦，你知道我们无法改变它？那么你明白了。” </p>
		<p>“哦，是的。是我设计了类体系。” </p>
		<p>“哦。”这使我彻底住了嘴。 </p>
		<p>“因为非同寻常的系统间交叉依赖，这个体系较之一般情况必须更加稳定，但允许你加入虚函数来避免类型分支（type-switching)代码。你可以通过加入新的间接层来解决问题。再次考虑那个比喻。 Personnel:: Accept是做什么的？”　 </p>
		<p>“呃，嗯？”我如坠雾中。 </p>
		<p>“这个类实施的是Poorly Named模式，或者说PNP。也被称作Visitor模式[1]。” </p>
		<p>“呃，嗯。”我现在明白了一些。“我曾经读到过Visitor模式。但那只是针对能够互相迭代访问的对象，不是么？” </p>
		<p>她叹了口气。“一个常见的误解。考虑一下V的含义，不是Visitor而是Virtual。”她解释道，“PNP最有用的地方，就是在不会进一步改变类体系的情况下增加虚拟函数。首先注意Accept在Personnel类和子类中是如何实现的。”她摘出如下代码：　 </p>
		<p>void Personnel::Accept( PersonnelV&amp; v ) { v.Visit( *this ); } void Officer::Accept ( PersonnelV&amp; v ) { v.Visit( *this ); } void Captain::Accept ( PersonnelV&amp; v ) { v.Visit( *this ); } void First::Accept ( PersonnelV&amp; v ) { v.Visit( *this ); } </p>
		<p>
				<br />她继续说：“Visitor基类是这样的：”　 </p>
		<p>class PersonnelV/*isitor*/<br />{<br />public:<br />  virtual void Visit( Personnel&amp; ) = 0;<br />  virtual void Visit( Officer&amp; ) = 0;<br />  virtual void Visit( Captain&amp; ) = 0;<br />  virtual void Visit( First&amp; ) = 0;<br />};</p>
		<p>“啊，”我想起来了，“所以当我编写多态地使用了Personnel派生的对象的代码时，只需调用Personnel::Accept(myVisitorObject)。因为Accept是虚函数，myVisitorObject.Visit会以正确派生类型调用，而且重载解析过程会选出正确地函数。这就像加入了一个新的虚函数，是么？”　 </p>
		<p>“的确是的，我的孩子。新加入的函数通常只需占用客户类的公共接口，但在其他任何方面都像虚函数一样，即使它并非成员函数。所需的一切只是初始类体系支持Accept，以使自己能够扩展。如果将来我们发现经常用新的子类扩展Personnel体系，或者希望更好地管理编译依赖，可以移植到Acyclic Visitor——除了稍许修改Accept函数，完全不必改变Personnel及其基类。[2, 3]　 </p>
		<p>我明白了。“OK，那我可以这样做。”我飞快的写道：　 </p>
		<p>class DoSomething : public PersonnelV<br />{<br />public:<br />  virtual void Visit( Personnel&amp; );<br />  virtual void Visit( Officer&amp; );<br />  virtual void Visit( Captain&amp; );<br />  virtual void Visit( First&amp; );<br />};<br />void DoSomething::Visit( Captain&amp; c )<br />{<br />  if( femaleGuestStarIsPresent )<br />    c.TurnOnCharm();<br />  else<br />    c.StartFight();<br />}<br />void DoSomething::Visit( First&amp; f )<br />{<br />  f.RaiseEyebrowAtCaptainsBehavior();<br />}</p>
		<p>Guru专心地盯着我的代码。“这些代码是为什么系统写的？” </p>
		<p>“啊……是个模拟器。” </p>
		<p>“我知道了。好的，继续吧，孩子。写剩下的吧。” </p>
		<p>我增加了一个测试例程：　 </p>
		<p>void f( Personnel&amp; p )<br />{<br />  p.Accept( DoSomething() ); // like 类似 p.DoSomething()<br />}<br />int main()<br />{<br />  Captain k;<br />  First s;<br />  f( k );<br />  f( s );<br />}</p>
		<p>Guru踱步离去，一边压了压耳后一络灰白的头发：“很好。确实，这个函数没有直接将原先的类体系局限于personnel::作用域。但在这时，这个函数不管使用任何名字都是虚拟的。”她笑着走开。她的声音随着身影慢慢消失。“Visitor模式即使采用了别的名字也一样有用，而且更容易描述。不过事情总是这样的……”　 </p>
		<p>
				<br />--------------------------------------------------------------------------------</p>
		<p>随着飞船的旋转，巨大的木星——它的壮美使木卫二表面相形见绌——渐渐进入了观察孔的视野。 </p>
		<p>“我将申请使用一会儿着陆服，”我决定：“能在这样的天空下走走该多好啊。即使我们只是旅行者。” </p>
		<p>“我们是旅行者。”珍妮赞成道：“不过我想知道我们是不是第一批到达的？” </p>
		<p>于是，一段时间里，我们只是默默的望着天空。　 </p>
		<p>
				<br />--------------------------------------------------------------------------------<br /></p>
<img src ="http://www.cppblog.com/ornaking/aggbug/15866.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ornaking/" target="_blank">ornaking</a> 2006-12-01 22:06 <a href="http://www.cppblog.com/ornaking/archive/2006/12/01/15866.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++名家对话 04</title><link>http://www.cppblog.com/ornaking/archive/2006/11/30/15823.html</link><dc:creator>ornaking</dc:creator><author>ornaking</author><pubDate>Thu, 30 Nov 2006 13:58:00 GMT</pubDate><guid>http://www.cppblog.com/ornaking/archive/2006/11/30/15823.html</guid><wfw:comment>http://www.cppblog.com/ornaking/comments/15823.html</wfw:comment><comments>http://www.cppblog.com/ornaking/archive/2006/11/30/15823.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ornaking/comments/commentRss/15823.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ornaking/services/trackbacks/15823.html</trackback:ping><description><![CDATA[
		<p>
				<br />--------------------------------------------------------------------------------</p>
		<p>“嗨！伙伴。”珍妮过来坐在我对面，周围一片狼藉，“还有别的传闻吗？” </p>
		<p>我看了看时间，只是想搞清楚确切时间。但我不想再逗留半个时。于是叫道：“嗨，伙计，没有了。他们不管三七二十一，在上面加了个盖，你下班这么早吗？” </p>
		<p>“布拉森科沃斯基睡不着，所以我就解脱了。我下次补他。” </p>
		<p>“他很有想法，”我嘴里又塞了一口饭，同意道，“我真是欣赏他。他总是干得很好，确保不会影响其它人。知道他自己在整个队伍里的位置。我希望在下个月在行星上着陆后还能跟他一块工作。每个人都应该象他这样敬业。” </p>
		<p>“大多数人都是这样的，”珍妮微笑道，“你自己也不懒啊。” </p>
		<p>“事实上，”我哈哈大笑，“这使我想起了鲍勃。对了，我已经跟你说起过他，他是我第一次工作中碰到的另一个怪人。有时候他的所作所为真让人想把他耳垂拧下来。说到点子上，当一个人做事一声不吭时我们就会说他在“拔活”你明白这个意思吗？ </p>
		<p>我继续解释...... </p>
		<p>
				<br />--------------------------------------------------------------------------------</p>
		<p>我身后的一个声音说道：“嗨，新来的，你破坏了整体结构。” </p>
		<p>我暗暗叹息了一声，闭起眼睛。我工作已经快两个月了，这个该死的程序员鲍勃对我傲慢的称呼，尤其是这声“新来的”，简单比Guru叫我“徒弟”更令我憎恨。我数到十，然后头也不回地回答：“你说什么啊，鲍勃？” </p>
		<p>“你最近的那些代码，”鲍勃和气地回答，呷了一口咖啡。“我的编译器在编译这些代码时有点问题，它抱怨说一个iterator已经被定义。你破坏了结构，最好在她发现前改正。”我有点吃不准，毕竟我还是个新手，我明白破坏整体结构不是件好事。我可以想象Guru的反应：记住，徒弟，团队合作的第二准则：你不能破坏整体结构。 </p>
		<p>“OK，好的，”我又叹了口气，这次发出了声音，“你说的是哪段代码？” </p>
		<p>鲍勃探身过来，按了几个键，拉出一个函数，看上去是这样的： </p>
		<p>// OBJMAP is a typedef for a standard container<br />void f( OBJMAP &amp;theMap )<br />{<br />    for( OBJMAP::iterator iter = theMap.begin();<br />         iter != theMap.end();<br />         ++iter ) {<br />        // do something<br />   }<br />   // other code in here<br />   for( OBJMAP::iterator iter = theMap.begin();<br />        iter != theMap.end();<br />        ++iter ) {<br />       // do something else<br />   }<br />}</p>
		<p>“有什么问题吗？”我问道，“我仔细看过Stroustrup的书，iterator在每个循环结束后应该失效。”</p>
		<p>“对，是这样，可在我的编译器上不是这样。”鲍勃答道，挖了挖牙齿，“它抱怨说iter已经被定义。很容易修正吧，虽然...只要去掉iter的第二个声明。事实上，我接手时，原先的代码就是这样的。然后你出现了，并破坏了整体结构。” </p>
		<p>这下我明白了。不过，由于我尚处于试用期，所以语气比较委婉。“鲍勃，”我耐心说理，“这在我们所有的目标平台上都能通过编译。我加上第二个声明是因为我的编译器抱怨说第二个iterator未被定义，应该象我那样做才行，Stroustrup的书上也是这样说的。” </p>
		<p>鲍勃看上去有点不耐烦：“我并不关心你的新式编译器。我那个编译器已经用了好几年了，它从没让我失望过。你的代码无法工作。” </p>
		<p>“鲍勃，代码没问题。它在我的编译器里工作得很好。” </p>
		<p>“那是你的编译器有问题。这不是我的错。我干我自己的活，我并不关心其它编译器。”鲍勃再次耸耸肩，语气中戒备心理更重。 </p>
		<p>一个轻轻的声音在我们身后响起：“我觉得你有几丝惧意。” </p>
		<p>我微微一震，鲍勃更吃惊，他的咖啡洒出了一些。Guru还在老远，正静静地向我们走来，跟往常一样，手里捧本大书。 </p>
		<p>Guru轻轻地摇了摇她那长了一头灰发的头，皱了皱眉，“害怕导致恼火，”她继续说道。 </p>
		<p>“啊，停。我才不怕其它编译器呢！”鲍勃激动地嚷道，舔了舔洒了手腕上的咖啡，“我才不理会它们呢。” </p>
		<p>“恼火导致憎恨。憎恨...导致磨难。” </p>
		<p>“是啊，我正受折磨呢。”我暗暗自语，“我只是想编写可移植的代码。对了，鲍勃，”我让步道，只想摆脱他的纠缠，“我会修改的，能让它工作，可以了吧？” </p>
		<p>“象我说的那样，新来的，去掉第二个声明。”他再次大声说道，然后转身离去。 </p>
		<p>温迪从隔壁小屋出来，看着他离去。“鲍勃这次在抱怨些什么？”她问我们。Guru站在附近，继续读那本大书。我向温迪展示了那段代码，并解释了一下鲍勃想做什么。“他就是这样，”温迪嗤了嗤鼻，“让他选择实现一个可移植的方案或者最适合他自己的编译器的方案，他总是选择后者。就象你说的，他的代码无法移植。对了，现在这个情况，你不必在每个循环处声明一个新的iterator。只要在第一个循环前声明一次，这样在所有的编译器里都能工作了。”她快速地在代码中作了一些修改： </p>
		<p>// OBJMAP is a typedef for a standard container<br />void f(OBJMAP &amp;theMap)<br />{<br />    OBJMAP::iterator iter;<br />    for( iter = theMap.begin();<br />         iter != theMap.end();<br />         ++iter ) {<br />        // do something<br />    }<br />    // other code in here<br />    for( iter = theMap.begin();<br />         iter != theMap.end();<br />         ++iter ) {<br />    // do something else<br />    }<br />}</p>
		<p>Guru抬起头，看了看这个解决方案。“很好，小姐。”温迪扬了扬眉，但只是微微的，她已经习惯了。“而且，正象你所提示的，这并不是唯一的解决方案。另一种更优雅的方案是完全避免iterator声明，而且可能看上去更清楚，就是用for_each和表达式模板（expression template)，只要有可能，就应该使用它们，徒弟。”Guru继续对我说，“重要的是要知道、理解并遵守神圣的标准，同样重要的是要熟悉自己的工具，即使有点离经叛道。鲍勃使用的编译器不支持在for语句初始式中声明变量。只要他还用他那个编译器，就没法照顾可移植代码。” </p>
		<p>“嗯...”我说道，擦了擦下巴，“编写可移植代码比我想象的要困难。所以我们不得不等鲍勃的编译器的新版本出来，才能使用我原先所写的代码。” </p>
		<p>Guru将目光投向远处，过了一会说道：“我不是先知，无法预见将来。然而，我是这样设想的：可移植性问题永远不会真正消失。编译器不会全部合丝合缝地符合标准。有些厂商甚至可能在它们的编译器里留有淘汰了的东西，目的是不破坏以前遗留的代码。” </p>
		<p>“同时，有些编译器将引入标准之外的新特性，在其内部或与其相关，这很好，否则这门语言会变得陈腐、不灵活，最终死于停滞。只基于该平台的程序可能会受益于该特性。在需要移植的代码中使用这些非标准的特性会不会带来灾难？有些新手，象你这样，可能会不经意地使用它们，而不曾注意到它们是无法移植的。不要太沉湎于某种编译器，以致害怕其它的编译器。害怕会导致不可移植。” </p>
		<p>她停了一下，接着转身离去。我们听到她你仍在说道：“我希望我能想起那个说过‘理论和实践的区别实际上要比在理论上更大一些...’这句话的智者的名字。” </p>
		<p>
				<br />--------------------------------------------------------------------------------</p>
		<p>“鲍勃可算不上个大人物，”我总结道。 </p>
		<p>“没人把鲍勃跟加尼米德联系在一起，”珍妮同意道，“没太多可原谅之处，只是一些。嗨，你得走了，安德森在等你呢。” </p>
		<p>我看了看时间，站了起来：“上班了。第三轮值班后你和劳拉在干什么？”我问道，我发现较之谈论鲍勃，甚至是Guru，还有些事情谈论起来更有趣。 </p>
		<p> <br /></p>
<img src ="http://www.cppblog.com/ornaking/aggbug/15823.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ornaking/" target="_blank">ornaking</a> 2006-11-30 21:58 <a href="http://www.cppblog.com/ornaking/archive/2006/11/30/15823.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++名家对话03</title><link>http://www.cppblog.com/ornaking/archive/2006/11/22/15564.html</link><dc:creator>ornaking</dc:creator><author>ornaking</author><pubDate>Wed, 22 Nov 2006 14:48:00 GMT</pubDate><guid>http://www.cppblog.com/ornaking/archive/2006/11/22/15564.html</guid><wfw:comment>http://www.cppblog.com/ornaking/comments/15564.html</wfw:comment><comments>http://www.cppblog.com/ornaking/archive/2006/11/22/15564.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ornaking/comments/commentRss/15564.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ornaking/services/trackbacks/15564.html</trackback:ping><description><![CDATA[
		<p>
				<br />--------------------------------------------------------------------------------</p>
		<p>我闯入船舱，“嗨!你听到一些传闻了吗？” </p>
		<p>珍妮的工作并没有丝毫的停顿：“草料（在英语中的发音跟嗨一样，珍妮的话带有几分嘲讽）是用来喂马的。什么传闻？” </p>
		<p>“关于加尼米德，”我解释道，“我听说他们在冰下发现了一些东西。没有迹象表明冰层近期曾融化过，说明那东西很古老了，而且它绝对不是大自然的产物。” </p>
		<p>这吸引了她的注意力。她直起身来，眼睛闪着光，“是吗？不属于人类的？” </p>
		<p>“有一种嗡嗡声。这是不是刺激了你的神经？” </p>
		<p>“是，嗯，还有其他的吗？” </p>
		<p>“没有其他的信息了，”我走到沙发前面，一屁股坐了下去，老老实实地回答，“这仅仅是一个传闻。而且，即使有什么大的事情发生，也不值得如此令人激动，不是吗？用不了两个月，我们都将在空间站里了。” </p>
		<p>“冰下面...?"珍妮思考着，“无论这个东西是不是人造物，至少有一点可以肯定，它已经有一段历史了。这会促使我们思考一些大的问题，不是吗？人类的历史，我们从哪里来，我们到哪里去。” </p>
		<p>“人类的起源和前进的方向，对。”我说，“这使我想起了发生在我的第一份工作期间的一些事情...” </p>
		<p>
				<br />--------------------------------------------------------------------------------</p>
		<p>那是一个阴天，我正在和温迪谈话。她是我的邻居...也就是说，我们的卧室相邻。 </p>
		<p>“对了,"我说，“我听说Stroustrup之所以命名这种语言为C++，是因为它是在C语言的基础上增加了一些东西，对吗？” </p>
		<p>“是这样的，”温迪说，“如果你再仔细考究一下这个名称的语义，你能想到什么呢？” </p>
		<p>一阵迷惘后，我只能摇摇头：“愿闻其详。” </p>
		<p>“你看，它使用了后缀形式的自增运算，意思是‘拿来C语言，增强它的功能，但还用原来的。'"我们一起笑了起来。 </p>
		<p>“一个不高明的玩笑。”Guru的声音把我们都吓了一跳。我们转过身，我有点紧张，但还是发现了一件不同寻常的事情：Guru的蓝眼睛里闪着光。她接着说：“对这个被你们嘲弄的语言，你们知道它的历史吗？” </p>
		<p>我放松下来，很快答道：“您的意思是...” </p>
		<p>Guru 合上了手中的书--从封面上看是一本D&amp;E[1]。她微微抬头，眼睛注视着远方然后开始慷慨陈词，像唱歌一样，瘦弱的身体随着演讲内容起伏晃动： </p>
		<p>“最初，计算机语言非常混乱，高级语言根本不存在，连固定的语言形式也没有。贝尔实验室的Richard Martin在使用了计算机语言的过程中意识到了高级语言的必要性。他深入地研究后，开发出了他认为不错的BCPL语言。 </p>
		<p>“然后Ken Thompson使用了BCPL，虽然他觉得很不错，但他认为如果想在一台PDP-7上使用BCPL，就必须精简BCPL。Ken Thompson深入地研究后，他开发出了一门新的语言，命名为B，它是BCPL的一个简化版本，他认为这是一门很好的语言。 </p>
		<p>“然而B语言没有类型的概念。Dennis Ritchie意识到了这一点，他深入研究后，对B语言进行了扩展。Ritchie 添加了结构和类型，他把这门语言叫作C语言,因为C是B的下一个字母，无论是在字母表还是在BCPL中。Ritchie 认为这门语言已经相当好了，但是他并不满足，继续投入大量的心血和汗水去完善这门语言。在1978年，Brian Kernighan 和Dennis Ritchie合作出版了《The C Programming Language.》[3]这为人们带来了很多的喜悦，人们看到了C的美妙，‘耶，这门语言真的很棒！’人们纷纷议论。 </p>
		<p>“C语言很快流传开来。新的特征不断的被添加，但并不是被所有的编译器厂商支持。人们开始感到沮丧，开始呼吁“我们需要标准C！” ANSI响应了这一要求，在1989年ANSI 宣布," 请注意，我将给所有的程序员带来快乐。因为在今天，C的标准X3.159-1989将诞生."接着ISO采纳了这一标准，发布了ISO/IEC 9899-1990。这又一次为人们带来喜悦。 </p>
		<p>“事情在进一步发展，早在C标准被发布之前，Bjarne Stroustrup就已经致力于改善C语言。Stroustrup致力于在C语言里增加类、函数参数类型检查和其他的一些优秀的特征。他继续深入，于1980年发布了'C With Classes.'这为人们带来了更多的喜悦和兴奋。 </p>
		<p>Stroustrup 并没有止步不前。他在对C语言做了很大的改变后，产生了一门新的语言，他命名这门语言为C++，就是C的增强的意思。他继续努力，在1986年出版了《The C++ Programming Language》,这再一次为人们带来了喜悦。 </p>
		<p>"象所有的事物一样，C++语言也在不断的进化着。模板，异常处理（exception handling）以及其它的特征陆续被添加到C++中，人们再次为新事物而兴奋。 </p>
		<p>“然而人们又开始抱怨了。那时候，不同的编译器开发商使用不同的解决方案支持模板和异常以及其它的特征，甚至有些开发商拒绝支持这些新特性。因此ISO又行动了， 在1998年----克林顿上台后第六年， 克雷蒂安成了除魁北克人之外所有加拿大人的总理。莱温斯基成了媒体的大红人，因为没有第二个辛普森诞生，那年没有什么大的新闻----在九月的第一天，ISO 宣布“ 请注意，我将给所有的程序员带来快乐的消息。因为在今天，C++的标准ISO/IEC 14882:1998(E)将诞生。”接着ANSI接受了这一建议，在七月的二十七号发布了几乎相同的标准，甚至早于ISO标准的发布，有时候事情就是这样。这又一次为人们带来喜悦，“啊，太好了，我们可以踩在巨人的肩膀上前进了”大家是这样欢呼的。 </p>
		<p>故事并未结束，当时Patrick Naughton为Sun Microsystems工作了一段时间后，深感厌烦，想离开Sun ，寻求新的发展。然而公司挽留了他，‘你可以拥有一支开发队伍，只要你愿意，一切都可以由你指挥，但要给我们带来点酷的东西。’于是一个名叫Green的团队产生了。 </p>
		<p>Green小组孤独地在荒野上不断的探索。他们寻求一种可用于嵌入式设备的面向对象语言，他们一开始在C++的基础上修改，但是C++的庞大使之无法满足他们的需要，于是他们在C++的基础上创建了一门新的语言Oak----这个命名仅仅因为James Gosling看到了相窗外的一颗橡树（Oak）。开发队伍仔细审视了这门语言，认为它相当的好。 </p>
		<p>也是在那个时代，美国巨型计算机应用中心开发出了Mosaic，这为我们带来了令人兴奋的WWW。随后Bill Joy试图公开Oak的源代码，使Oak能使用于网页浏览。Sun审视了这个想法，觉得不错，但Oak这个商标已经被人占用，所以Sun把这门新语言称为Java，并发布了《the Java programming Language》。这又一次使人们激动,"耶！我们又有了一个真正与开发平台无关的语言！我们认为，这真是太酷了!" </p>
		<p>Guru 结束了她的演讲，把目光转向了我，“年轻人，这就是C语言家族的故事。”她轻轻的把遮住耳朵的一缕灰发拨开，静穆的站了一会儿，重新低下了头，打开她的D&amp;E，静静的走开了。 </p>
		<p>我被震住了，呆呆的站了好一会才看了看温迪。 </p>
		<p>“嗨，不要看我，”温迪耸了耸肩，“你会慢慢熟悉她的。她是我共事过的程序员中最好的。” </p>
		<p>我几乎不能相信。我试图想记起装有我的软盘放在哪了。 </p>
		<p>
				<br />--------------------------------------------------------------------------------</p>
		<p>珍妮开玩笑似的笑了起来：“你花了多少时间----工作还是思考如何离她远点？” </p>
		<p>“是的，”我傻笑了一下。那不是我最后一次同珍妮谈论关于Guru或者其它更令人高兴的的话题， <br /></p>
<img src ="http://www.cppblog.com/ornaking/aggbug/15564.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ornaking/" target="_blank">ornaking</a> 2006-11-22 22:48 <a href="http://www.cppblog.com/ornaking/archive/2006/11/22/15564.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++名家对话 02</title><link>http://www.cppblog.com/ornaking/archive/2006/11/21/15498.html</link><dc:creator>ornaking</dc:creator><author>ornaking</author><pubDate>Tue, 21 Nov 2006 02:29:00 GMT</pubDate><guid>http://www.cppblog.com/ornaking/archive/2006/11/21/15498.html</guid><wfw:comment>http://www.cppblog.com/ornaking/comments/15498.html</wfw:comment><comments>http://www.cppblog.com/ornaking/archive/2006/11/21/15498.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ornaking/comments/commentRss/15498.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ornaking/services/trackbacks/15498.html</trackback:ping><description><![CDATA[
		<p>
				<br />--------------------------------------------------------------------------------</p>
		<p>在船上，你最后想感受的就是海风了。我和珍妮碰巧很不凑巧，最接近事故发生地点。我们费尽力气，将笨重的房门关上，并将其密封，使打破的小房间与外面隔绝。当我们靠在门上，作深呼吸的时候，汽笛突然停止鸣响. </p>
		<p>“他奶奶的，到底怎么回事？”值班驾驶员刺耳的声音穿过了整个船舱。 </p>
		<p>“小小渗漏而已，先生，”珍妮回答，“我们已封好了那间船厢，问题已解决，没事了。” </p>
		<p>“你密封了船厢？为什么它不能自动密封呢？刚才我们不是把门重修了吗？” </p>
		<p>珍妮和我对望了一眼。“哦，先生，维修人员都认为这个新的锁闭装置不会有问题，因为它是刚从工厂出来的，是崭新的，因此他们没有检查它。它是好的，只不过他们没有取下所有的包装材料，所以它不能自动密封。” </p>
		<p>稍停片刻后，珍妮接着说，“对，干得不错。有个维修人员正在赶来。” 接着，恰好在被打破的船舱前面，我们听到XO的声音在说：“约翰逊先生，叫工程部主管到我的船舱来见我。告诉雷利到...” </p>
		<p>我对着珍妮咧嘴一笑，“能听到刚才的对话，我愿意付一个星期的薪水。它提醒了我我们以前遇到的一个编码安全的问题，这是很久以前的事了...” </p>
		<p>
				<br />--------------------------------------------------------------------------------</p>
		<p>我的第一份编程工作已干了几个星期了，也熟悉了同一项目组的其他的程序员，其中一个是鲍勃。几乎在每一个方面，他都与Guru形成鲜明对比。鲍勃是个自命不凡的家伙，他的代码让其他人很难维护，并且经常违反编程的规则。 </p>
		<p>我已经一头扎进我的第一个项目，鲍勃的任务是对我经手的一些资料进行核查。他来到我的桌边，拿着一杯咖啡，靠在隔板上，“你的代码由于在你的helper函数里存在一个存取违规而崩溃。”他说，“在Space Cadet知道之前，你最好把它改好。” </p>
		<p>“Guru的确是一个优秀的程序员，鲍勃，”我生气了。的确，Guru是很古怪，在她旁边时，我也不自在。每次我和她谈话时，我总是不断考虑更新我的概略，但是鲍勃刚才的态度莫名其妙地激怒了我。 </p>
		<p>“是，不管怎样，”鲍勃轻蔑地晃了晃杯子，溅出一点咖啡，“总之，你的helper函数使用了空的指针。让我们来看一看。” </p>
		<p>“但helper函数并没有使用任何指针啊，”我皱了皱眉头。“只不过是一个对xWrapper类的引用而已。”我在文本编辑器里找出代码。它看起来是这样的： </p>
		<p>class xWrapper<br />{<br />/* ... */<br />public:<br />virtual void dump() { cout &lt;&lt; name &lt;&lt; endl; } <br />}; <br />void helper( xWrapper&amp; w )<br />{<br />w.dump(); <br />// ... do other stuff with w ... <br />}</p>
		<p>鲍勃将头伸向显示器，“是的，就是在dump()语句这里崩溃。”他抓了抓鼻子说。 </p>
		<p>“那是不可能的。” </p>
		<p>“确实是这样，年轻人。” 鲍勃真的让我心烦意乱，“我所做的就是从类工厂中取得一个新指针，把它传递给你，而你就对它进行提领操作。如果那个指针是个空指针，你就引用了一个空指针。你再仔细看看。” </p>
		<p>“嗯，是吗，”我支吾道，信心有点动摇。毕竟，我刚从学校出来，而鲍勃已经有几年的经验了。“我想要注意到引用是否为空是不可能的。” </p>
		<p>“你所要做的，”鲍勃说，“就是检查引用的地址。象我说的，如果想要安全，你就应该检查它...” </p>
		<p>“不，”Guru平静的声音把我们两人都吓一跳。她又一次出现在恰当的时候。现在，她正站在我们身后，手里拿着一本打开的大书。“没必要这么麻烦。标准告诉我们不存在空引用。” </p>
		<p>“但它这是可能的，”鲍勃坚持道，“我们刚刚展示显示了这一点，是他的问题。” </p>
		<p>我真想打他几耳光。Guru只是冷眼看了他一下。“不，你应该检查那个。绝不要提领一个空指针。改一下你的代码：如果指针是空的，就不要调用helper()。今天下午把能够运行的代码给我看看。让这种难看的程序方法滚蛋。”她挥挥手让他离开。 </p>
		<p>鲍勃眨了眨眼。但她是这组里的高级程序员，因此他什么也没说就走开了。 </p>
		<p>“我的学徒工，”她继续单独地跟我说，“使用引用代替指针的主要的理由之一就是把你从不得不测试它是否引用一个无效的目标的负担中解放出来。空引用的唯一来源就是提领一个空指针——这在神圣的标准里明确被禁止，在未定义的行为(Undefined Behavior)里也写的很清楚。” </p>
		<p>“OK，但是为什么只告诉我？为什么不告诉鲍勃呢？” </p>
		<p>她悲伤地摇摇头。“他知道这个。我说服不了他 ，他已经沉湎于未定义的行为了。我的学徒工，小心踏入未定义行为这条路。一旦你走上这条路，你就会受它控制，当你试图捕捉并且解决问题时，它将耗费你的时间。” </p>
		<p>我心中暗自掂量。“好，”我问道，“创建一个无效的引用会有什么样的危险？比方说，你传递了一个对象给一个函数，但你传的那个对象在函数结束前超出了它的范围。你怎样预防这种情况的出现呢？” </p>
		<p>她摇了摇头：“这个办不到。从这方面来说，引用和指针是很类似的。想想这个比喻。”Guru很快地在我的白色书写板写下了一些代码： </p>
		<p>T* f() <br />{ <br />T t; <br />return &amp;t; //返回一个悬而未决的指针<br />} </p>
		<p>void f1( T* t ) <br />{ <br />if( t ) <br />t-&gt;doSomething(); <br />}</p>
		<p>int main() <br />{ <br />T *tPtr = f(); <br />f1( tPtr ); // 非法<br />}</p>
		<p>“这个f()函数显然为人所诟病。”她理了理耳后一络银灰的头发，接着说，“它返回一个指向局部对象的指针，当函数结束时，该变量就超出了其作用范围。因此tPtr在main()里指向一个已被删除的对象。f1()函数尽了最大努力——它对空指针进行了检查。一些平台也提供编译器专用函数来检测一个指针是否指向一块有效的内存区域。但是，这些函数还是无法检测到上面例子中的那种情况。在f1（）内部，变量t没有指向一个有效的T类型的对象，因此f1（）函数无法确定t是否有效。 因此当程序员写f1（）时，要相信你的程序员同事们会尽他们最大努力，确保向你提供的指针是指向一个有效的对象的。 </p>
		<p>“就像你必须假定一个非空的指针是有效的一样，你必须假定一个引用也是有效的。在你的程序员同事中你必须要有信心。” </p>
		<p>“即使是鲍勃？”我问道。 </p>
		<p>她用一种悲伤的表情点点头，然后看着她那本大部头书沿着走廊飘然而去。 </p>
		<p>
				<br />--------------------------------------------------------------------------------</p>
		<p>“鲍勃还是坚持已见吗？”珍妮问道 </p>
		<p>“比他应该坚持的时间要长。”我停止微笑，“我们公司也做一些软件，是用于起搏器的。你知道，这种设备是要植入到胸腔里的。有一次，他测试一段代码，还没等这些代码验证所有的先决条件，就整合到产品中了。并且...” </p>
		<p>珍妮耸了耸眉毛。 </p>
		<p>我悲哀地点点头：“鲍勃最后又找到了别一份工作，是做零售的。他应该高兴才是,国为这是在程序员责任法颁布之前的事。”这时，事故控制小组接过我们的工作，我们作好准备，继续进行监视。 </p>
		<p>这不是我最后一次和珍妮讨论有关Guru，或更多令人愉快的事。 </p>
<img src ="http://www.cppblog.com/ornaking/aggbug/15498.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ornaking/" target="_blank">ornaking</a> 2006-11-21 10:29 <a href="http://www.cppblog.com/ornaking/archive/2006/11/21/15498.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++名家对话01</title><link>http://www.cppblog.com/ornaking/archive/2006/11/20/15451.html</link><dc:creator>ornaking</dc:creator><author>ornaking</author><pubDate>Mon, 20 Nov 2006 03:53:00 GMT</pubDate><guid>http://www.cppblog.com/ornaking/archive/2006/11/20/15451.html</guid><wfw:comment>http://www.cppblog.com/ornaking/comments/15451.html</wfw:comment><comments>http://www.cppblog.com/ornaking/archive/2006/11/20/15451.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ornaking/comments/commentRss/15451.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ornaking/services/trackbacks/15451.html</trackback:ping><description><![CDATA[
		<p>
				<br />--------------------------------------------------------------------------------</p>
		<p>我刚刚在昨天遇到珍妮，就在人员中转站，现在已经远远在我们脚下了。“我会永远记住我的第一个工作，”在乘务员检查完我们的安全带后，我对她说。 </p>
		<p>“想起了什么” </p>
		<p>“项目组高级程序员，”我微笑着说，陷入了回忆中。“她是个古怪的家伙，我们都叫她做Guru。领导不喜欢把新来的程序员分到她的组里；我是当年招聘的四个人中唯一坚持到试用期结束的。” </p>
		<p>珍妮扭过头刚准备问一个问题，这时钟声响起，隆隆的加速声响彻整个机舱，我们的谈话因此中断了好几分钟，等这截推进器快烧完时，我们已飞离轨道，我给她讲述了工作第二天发生的故事。 </p>
		<p>
				<br />--------------------------------------------------------------------------------</p>
		<p>我们用早期的C++语言编程。工作的第二天中午，厌烦了读职工手册，于是我写了一个工具类，里面包含一个原始指针作为成员变量： </p>
		<p>#include "xStruct.h" // definition of struct X</p>
		<p>class xWrapper<br />{<br />    X* xItem;<br />public:<br />    xWrapper() : xItem(new X) { }<br />    ~xWrapper() { delete xItem; }</p>
		<p>    void dump() { /* dumps xItem to cout */ }<br />};</p>
		<p>当然了，使用这个类的程序由于内存问题总是时不时的崩溃，因为我违反三个重要设计原则之一：任何时候，只要你提供了析构函数、拷贝构造函数或赋值运算符中的一个，你通常需要三个都提供。([1]) “所以，”我自言自语道，“我必须自己处理拷贝和赋值问题。简单地...auto_ptr有拷贝构造函数和赋值运算符，我可以拿过来用一下。”（你知道早期C++程序库中的auto_ptr，是吗？） </p>
		<p>既然auto_ptr自动删除它所指向的对象，我只需要改变xItem的类型，移去析构函数中的delete语句－auto_ptr会处理其他的事情，对吗？ </p>
		<p>Class xWrapper<br />{<br />    auto_ptr&lt;X&gt; xItem;<br />public:<br />    xWrapper() : xItem(new X) { }<br />    void dump() { /* dumps xItem to cout */ }<br />};</p>
		<p>不幸的是，程序仍要崩溃，这次是由于它试图对空指针进行提领操作。我对这个问题苦苦思索了半个小时，这时Guru碰巧从我这里路过，像芦柴棒一样瘦瘦的她一只手里捧着厚厚的一本打开的书。 她来得――我的意思是她来得太不是时候了，真是怕什么来什么，实际上，这简直称得上诡异了。 </p>
		<p>“哦，你在看什么？”我指着书问，想让她的注意力从我的屏幕挪开，同时也希望着她能离开。 </p>
		<p>Guru眨了眨眼睛。“Josuttis的书，”她边温和地说着，边做了个标记并合上书。“年轻人，你写了些什么啊？” </p>
		<p>“我在写这个wrapper　class时遇到了问题，”我承认道，“我使用了auto_ptr成员，但是在测试时，不知为什么它的指针重置为null。” </p>
		<p>“把你的代码给我看一下，”Guru说。我把屏幕转向她。“所有权，”仅仅扫了一眼，她立刻说， </p>
		<p>这回轮到我眨眼睛了。 </p>
		<p>“所有权，孩子；你的问题是所有权语义学。一臣不事二主，没有指针可以同时给两个auto_ptr使用。” </p>
		<p>她的话虽然很怪，却使我意识到了自己的错误。“哦，是的，”我答道。“当你拷贝一个auto_ptr的时候，原来的那个放弃了所有权，重置为null。Xwrapper的拷贝构造函数使用了那个缺省的行为，所以原来xWrapper对象的auto_ptr被重置，于是我存取它的时候，实际上是在提领一个null指针。” </p>
		<p>“对的，”Guru说。“你能使用标准里已有的代码，这很好，不过使用的时候要小心。对于xWrapper来说，你还是必须自己写拷贝构造函数和赋值运算符。” </p>
		<p>“但是，我没法用auto_ptr的拷贝构造函数和赋值运算符来实现他们啊，因为auto_ptr自己的版本无法正确的……－哦。有办法了。我可以用auto_ptr的提领运算符访问其拥有的对象。”我很快写下了下面的两个函数： </p>
		<p>XWrapper::xWrapper(const xWrapper&amp; other)<br />: xItem(new X(*other.xItem))<br />{ }</p>
		<p>xWrapper&amp; xWrapper::operator=(const xWrapper &amp;other)<br />{<br />*xItem = *other.xItem;<br />}</p>
		<p>
				<br />“嗨，cool。”我喜欢这个实现，“我甚至不需要在赋值运算符里检查自我赋值。” </p>
		<p>“很好。” </p>
		<p>我应该就此打住，闭紧嘴巴，可惜我当时正得意着呢:“使用auto_ptr很容易出错。如果在我实际上并不想发生所有权转移时，它可以告诉我它将试图转移所有权，那有多好啊……”。” </p>
		<p>“冷静一下！”Guru打断了我。“这不是auto_ptr的错。如果你想达到这个效果，你应该明确地说明你不想auto_ptr被拷贝。” </p>
		<p>“但是怎么去做呢？这是不可能的。” </p>
		<p>“可能的。记住const修饰符的使用。声明一个auto_ptr不可变的方法是使它成为const。假如你让成员成为一个const，编译器就不能不声不响地产生xWrapper对象的一个拷贝。或者，你可以使用一个也许叫strict_auto_ptr的修正版本，这样编译器就不会错误地拷贝和赋值xWrapper。当然，在这种情况下，让它成为const比较简单和有效率。” ([2]) </p>
		<p>她离开的时候又重新打开Josuttis的书继续看了起来，边走边心不在焉地和我说这话。她和她的声音慢慢地远去：“要注意的是，我的孩子...auto_ptr是一个有用的工具，但是就象你刚才发现的那样，它不是万能的。好好琢磨Josuttis chapter 4.[3]，永远不要在标准程序库的容器中用auto_ptr，如vector&lt;auto_ptr&gt;，因为auto_ptr的拷贝和赋值不能达到标准的要求。此外，永远不要用auto_ptr指向对象数组，因为auto_ptr的析构函数用non-array delete删除所拥有的对象；对于对象数组来说，可以用一个vector。程序库...” </p>
		<p>这时她转了个弯，消失了。这只是我工作的第二天；我告诉自己，不能空闲下来，我应该不断地学习，前面的路还很长！ </p>
		<p>
				<br />--------------------------------------------------------------------------------</p>
		<p>“不可思议，”珍妮说，喝着咖啡，此时飞船已飞离了泰兰的交通控制区域，并继续加速，“那么，你离开了吗？” </p>
		<p>“她...我不确定为什么，”我坦白承认，“这种事发生了好几次。我也想和其他人那样在试用期离开，尽管他可能对我有好的影响。你曾经和这样的怪人工作过吗？” </p>
		<p>“嗯，我想也有一些。” </p>
		<p>这不是最后一次我和珍妮谈论Guru或其他更令人高兴的事。 　 </p>
<img src ="http://www.cppblog.com/ornaking/aggbug/15451.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ornaking/" target="_blank">ornaking</a> 2006-11-20 11:53 <a href="http://www.cppblog.com/ornaking/archive/2006/11/20/15451.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>