C++ Gotchas 条款1及条款17

翻译文档 专栏收录该内容
74 篇文章 0 订阅

C++ Gotchas 条款1及条款17

Stephen C. Dewhurst

 

Gotcha条款1:过渡注释

 

有许多注释其实是不必要的。其一般都会使得源代码难于阅读和维护,并常常将维护人员引入歧途。考查下面这个简单的语句:

 

a = b;  // b 赋值给 a

 

比起代码本身,这个注释并不能传达更清晰的语句含义,因此是没有用的。实际上,它比没有用还糟。它是致命的。其一,该注释转移了读者投向代码的注意力,增加了读者为获知代码含义所要费心过目的文字量;其二,导致有更多的文本需要维护,因为注释必须随着其对应代码的改变而改变以得到维护;其三,这种对注释的必要维护经常没有被实施。

 

c = b; // b 赋值给 a

 

一位细心的维护人员不会简单的认为该注释存在谬误,因而不得不通过追踪程序来确认注释是否是错误的、非常规的(c是指向areference)或微妙的(对c赋值会导致后来将相同的赋值传播给a)。该行代码本来应该不需要注释:

 

a = b;

 

该代码就其本身而言是足够清晰的,没有会被不当维护的注释。这在道义上近似于“最高效的代码就是那些不存在的代码”这句陈年老话。同样的道理适用于注释:最好的注释就是那些不必被撰写的注释,因为代码本身是自文档化的(self-documenting)(若非如此,则早就用注释来描述它了)。

 

另一些常见的有关不必要注释的示例频繁出现在类别定义里,其要么是糟糕的编码规范造成的恶果,要么就是C++菜鸟的杰作。

 

class C {

 // Public Interface

 public:

   C(); // default constructor

   ~C(); // destructor

   // . . .

};

 

你感觉你好像在阅读某人的幼儿手稿。如果一个维护人员需要被时刻提示public:的含义,你一定不希望由这个人来维护你的代码。上述注释没有哪一条能为有经验的C++程序员提供任何帮助,只会给代码添乱并提供更多会被不当维护的文本。

 

class C {

 // Public Interface

 protected:

   C( int ); // default constructor

 public:

   virtual ~C(); // destructor

   // . . .

};

 

程序员总有很强烈的动机,不希望“浪费”源代码文本的行数。据称,如果一个建构单位(函数、类别的公共接口,等等)能够以惯用且合理的格式在一个约3040行的“页面”上面显示出来,那么其代码就很容易理解;如果该代码延长至第二页,理解它的难度就是原来的两倍;而如果该代码延长至第三页,理解它的难度就大约是原来的四倍。

 

还有一个可恶的做法就是,在源代码文件的首部或尾部以注释的方式插入变更履历:

 

/* 6/17/02 SCD fixed the gaforniflat bug */

 

这到底是有用的信息,还是其维护人员吹的牛?该注释没有任何用处,无论插入它的这一两周以来发生了什么,但它会可怕的赖在那里多年,影响一代又一代的维护人员。一个好得多的替代方案是将这些注解工作留给你的版本控制软件去做;C++源代码文件不是存放条目清单的地方。

 

要避免不必要的注释,并使代码清晰且易维护,最好的方法之一就是遵循简单、定义良好的命名规约,并选择明晰的名称,使其能够反映所指代实体(函数、类别、变量,等等)的抽象含义。在声明中使用规整的参数名称特别重要。考虑下面接收三个可辨识型别之参数的函数:

 

/*

 Perform the action from the source to the destination.

 Arg1 is action code, arg2 is source, and arg3 is destination.

 由源位置到目标位置实施操作。

 第一个参数是操作代码,第二个参数是操作源,第三个参数是操作目标。

*/

void perform( int, int, int );

 

不算太糟,但想想如果函数有七八个参数而不是三个的时候会怎么样呢。我们可以做得更好:

 

void perform( int actionCode, int source, int destination );

 

这样好些,尽管我们可能还是会遇到一个爱打趣的人来告诉我们该函数会做什么(尽管不是告诉我们函数怎么做)。声明中规整的参数名称最吸引人的一个地方在于它不同于注释,它通常能够随其余的代码被一同维护,尽管它对代码的含义没有任何影响。我无法想象出单独一个程序员如何能够在没有同时改变相应参数名称的情况下将第二个和第三个参数的含义弄反,但我可以确定,有众多的程序员会在没有维护相应注释的情况下对调两者的含义。

 

或许Kathy StarkProgramming in C++中说得最好:“如果能在程序中使用有意义且助记的名称,那么就只会在偶尔的情况下才需要额外的注释。而如果没有使用有意义的名称,那么附加的注释也不会使代码更容易理解。”

 

另一个将注释最少化的方法是,采用标准的或者众所周知的组件:

 

printf( "Hello, World!" ); // 列印 "Hello, World" 到屏幕上

 

这段注释即没用,也很难保持正确性。关键不在于标准组件必需是自文档化的(self-documenting),而在于它们早就被妥当归档并且众所周知了。

 

swap( a, a+1 );

sort( a, a+max );

copy( a, a+max, ostream_iterator<T>(cout,"/n") );

 

swap,sort以及copy是标准的组件,因此插入额外的注释只会扰乱源代码,并引入对标准操作的不精确描述。

 

注释并不是天生就有害的?注释经常是必要的?但它们必须被维护,而且它们总是比所评注的代码更难维护。注释不应该表述显而易见的事情,也不应该提供那种能在其它地方做更佳维护的信息。我们的目标并非不择手段的消去注释,而是要采用最少量的注释让代码变得更容易理解和维护。

 

 

Gotcha条款17Maximal Munch问题

 

如果遇到这样的表达式你怎么办:

 

++++p->*mp

 

你是否处理过“Sergeant operator[译注1]

 

template <typename T>

class R {

  // . . .

  friend ostream &operator <<< // a sergeant operator?

    T >( ostream &, const R & );

};

 

你有没有考虑过下面表达式的合法性?

 

a+++++b

 

欢迎来到maximal munch [译注2]的世界。在C++编译的前期阶段中,编译器里施行“词法分析(lexical analysis)”的模块[译注:即词法分析器]的任务是,将输入流断开成为“words(单字)”或称token(字元)。当遇到像 ->* 这样的字符序列时,词法分析器可以将其辨识成三个token-,>以及*),或者两个token->*),或者一个token->*)。为了避免这种含混不明的状况,词法分析器总是辨识最长的token,尽可能多的读过其所能合法读取的字符——这就是maximal munch

 

表达式 a+++++b 是不合法的,因为其被分解为 a ++ ++ + b,对 a++ 这样的 rvalue 施以 post-increment(后增操作)是不合法的。如果你本来是想先对apost-increment(后增操作),再把其结果加上对b pre-increment(先增操作)的结果,那么你必须在表达式中加入至少一个空格:a+++ ++b。如果你对阅读你代码的读者还有那么一点点尊重,你就得再引入另一个空格,尽管其并不必要:a++ + ++b;另外,也没有人会责怪你再加入几个圆括弧:(a++) + (++b)

 

Maximal munch 所解决的问题比它所产生的问题要多,但在两个常见的情况下,它有些恼人。第一种情况是,templates实体化的时候,其所带的参数本身又是被实体化的templates。例如,一个人可能希望运用标准程序库来声明一个string vector list

 

list<vector<string>> lovos; // 错的!

 

不幸的是,实体化表达式中的两个邻接的右尖括号被解释成一个移位操作符,我们会遇到语法错误。这里需要增加空格:

 

list< vector<string> > lovos;

 

第二种情况涉及到针对pointer formal arguments运用 default argument initializers

 

void process( const char *= 0 ); // error!

 

这个声明企图在formal argument declaration中使用赋值运算符 *=。这是个语法错误。其属于“wages of sin”一类问题,即是说:如果代码的作者给这个formal argument一个名称的话,就不会出现错误。这个名称不仅可以为自身提供最佳的文档化说明,也杜绝了maximal munch问题:

 

void process( const char *processId = 0 );

 

关于作者

Stephen C. Dewhurst (<www.semantics.org>) 是坐落于Massachusetts东南部酸果泽中的Semantics Consulting, Inc.的总裁。他专精于C++咨询,高阶C++编程、STL和设计模式的培训。Steve还是The C++ Seminar的主要讲师之一。

注解

[译注1]Sergeant operator(军士操作符),这是一个很诙谐的说法,因为代码中本来是一个template function的声明,结果排列出来的时候,operator << template所带的 < 连起来形成 operator <<<,活象个军衔标志。

[译注2]maximal munch,最大化吞取。

 

S. Dewhurst, C++ Gotchas (#1 and #17). c 2003 Pearson Education, Inc. All rights reserved.

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
<p> <span style="font-size:14px;color:#337FE5;">【为什么学爬虫?】</span> </p> <p> <span style="font-size:14px;">       1、爬虫入手容易,但是深入较难,如何写出高效率的爬虫,如何写出灵活性高可扩展的爬虫都是一项技术活。另外在爬虫过程中,经常容易遇到被反爬虫,比如字体反爬、IP识别、验证码等,如何层层攻克难点拿到想要的数据,这门课程,你都能学到!</span> </p> <p> <span style="font-size:14px;">       2、如果是作为一个其他行业的开发者,比如app开发,web开发,学习爬虫能让你加强对技术的认知,能够开发出更加安全的软件和网站</span> </p> <p> <br /> </p> <span style="font-size:14px;color:#337FE5;">【课程设计】</span> <p class="ql-long-10663260"> <span> </span> </p> <p class="ql-long-26664262" style="font-size:11pt;color:#494949;"> 一个完整的爬虫程序,无论大小,总体来说可以分成三个步骤,分别是: </p> <ol> <li class="" style="font-size:11pt;color:#494949;"> 网络请求:模拟浏览器的行为从网上抓取数据。 </li> <li class="" style="font-size:11pt;color:#494949;"> 数据解析:将请求下来的数据进行过滤,提取我们想要的数据。 </li> <li class="" style="font-size:11pt;color:#494949;"> 数据存储:将提取到的数据存储到硬盘或者内存中。比如用mysql数据库或者redis等。 </li> </ol> <p class="ql-long-26664262" style="font-size:11pt;color:#494949;"> 那么本课程也是按照这几个步骤循序渐进的进行讲解,带领学生完整的掌握每个步骤的技术。另外,因为爬虫的多样性,在爬取的过程中可能会发生被反爬、效率低下等。因此我们又增加了两个章节用来提高爬虫程序的灵活性,分别是: </p> <ol> <li class="" style="font-size:11pt;color:#494949;"> 爬虫进阶:包括IP代理,多线程爬虫,图形验证码识别、JS加密解密、动态网页爬虫、字体反爬识别等。 </li> <li class="" style="font-size:11pt;color:#494949;"> Scrapy和分布式爬虫:Scrapy框架、Scrapy-redis组件、分布式爬虫等。 </li> </ol> <p class="ql-long-26664262" style="font-size:11pt;color:#494949;"> 通过爬虫进阶的知识点我们能应付大量的反爬网站,而Scrapy框架作为一个专业的爬虫框架,使用他可以快速提高我们编写爬虫程序的效率和速度。另外如果一台机器不能满足你的需求,我们可以用分布式爬虫让多台机器帮助你快速爬取数据。 </p> <p style="font-size:11pt;color:#494949;">   </p> <p class="ql-long-26664262" style="font-size:11pt;color:#494949;"> 从基础爬虫到商业化应用爬虫,本套课程满足您的所有需求! </p> <p class="ql-long-26664262" style="font-size:11pt;color:#494949;"> <br /> </p> <p> <br /> </p> <p> <span style="font-size:14px;background-color:#FFFFFF;color:#337FE5;">【课程服务】</span> </p> <p> <span style="font-size:14px;">专属付费社群+定期答疑</span> </p> <p> <br /> </p> <p class="ql-long-24357476"> <span style="font-size:16px;"><br /> </span> </p> <p> <br /> </p> <p class="ql-long-24357476"> <span style="font-size:16px;"></span> </p>
<div style="color:rgba(0,0,0,.75);"> <span style="color:#4d4d4d;"> </span> <div style="color:rgba(0,0,0,.75);"> <span style="color:#4d4d4d;"> </span> <div style="color:rgba(0,0,0,.75);"> <div style="color:rgba(0,0,0,.75);"> <span style="color:#4d4d4d;">当前课程中商城项目的实战源码是我发布在 GitHub 上的开源项目 newbee-mall (新蜂商城),目前已有 6300 多个 star,</span><span style="color:#4d4d4d;">本课程是一个 Spring Boot 技术栈的实战类课程,课程共分为 3 大部分,前面两个部分为基础环境准备和相关概念介绍,第三个部分是 Spring Boot 商城项目功能的讲解,让大家实际操作并实践上手一个大型的线上商城项目,并学习到一定的开发经验以其中的开发技巧。<br /> 商城项目所涉的功能结构图整理如下:<br /> </span> </div> <div style="color:rgba(0,0,0,.75);">   </div> <div style="color:rgba(0,0,0,.75);"> <p style="color:#4d4d4d;"> <img alt="modules" src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9uZXdiZWUtbWFsbC5vc3MtY24tYmVpamluZy5hbGl5dW5jcy5jb20vcG9zdGVyL3N0b3JlL25ld2JlZS1tYWxsLXMucG5n?x-oss-process=image/format,png" /> </p> </div> <p style="color:rgba(0,0,0,.75);"> <strong><span style="color:#e53333;">课程特色</span></strong> </p> <p style="color:rgba(0,0,0,.75);">   </p> <div style="color:rgba(0,0,0,.75);">   </div> <div style="color:rgba(0,0,0,.75);"> <ul> <li> 对新手开发者十分友好,无需复杂的操作步骤,仅需 2 秒就可以启动这个完整的商城项目 </li> <li> 最终的实战项目是一个企业级别的 Spring Boot 大型项目,对于各个阶段的 Java 开发者都是极佳的选择 </li> <li> 实践项目页面美观且实用,交互效果完美 </li> <li> 教程详细开发教程详细完整、文档资源齐全 </li> <li> 代码+讲解+演示网站全方位保证,向 Hello World 教程说拜拜 </li> <li> 技术栈新颖且知识点丰富,学习后可以提升大家对于知识的理解和掌握,可以进一步提升你的市场竞争力 </li> </ul> </div> <p style="color:rgba(0,0,0,.75);">   </p> <p style="color:rgba(0,0,0,.75);"> <span style="color:#e53333;">课程预览</span> </p> <p style="color:rgba(0,0,0,.75);">   </p> <div style="color:rgba(0,0,0,.75);">   </div> <div style="color:rgba(0,0,0,.75);"> <p style="color:#4d4d4d;"> 以下为商城项目的页面和功能展示,分别为: </p> </div> <div style="color:rgba(0,0,0,.75);"> <ul> <li> 商城首页 1<br /> <img alt="" src="https://img-bss.csdnimg.cn/202103050347585499.gif" /> </li> <li> 商城首页 2<br /> <img alt="" src="https://img-bss.csdn.net/202005181054413605.png" /> </li> <li>   </li> <li> 购物车<br /> <img alt="cart" src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9uZXdiZWUtbWFsbC5vc3MtY24tYmVpamluZy5hbGl5dW5jcy5jb20vcG9zdGVyL3Byb2R1Y3QvY2FydC5wbmc?x-oss-process=image/format,png" /> </li> <li> 订单结算<br /> <img alt="settle" src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9uZXdiZWUtbWFsbC5vc3MtY24tYmVpamluZy5hbGl5dW5jcy5jb20vcG9zdGVyL3Byb2R1Y3Qvc2V0dGxlLnBuZw?x-oss-process=image/format,png" /> </li> <li> 订单列表<br /> <img alt="orders" src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9uZXdiZWUtbWFsbC5vc3MtY24tYmVpamluZy5hbGl5dW5jcy5jb20vcG9zdGVyL3Byb2R1Y3Qvb3JkZXJzLnBuZw?x-oss-process=image/format,png" /> </li> <li> 支付页面<br /> <img alt="" src="https://img-bss.csdn.net/201909280301493716.jpg" /> </li> <li> 后台管理系统登录页<br /> <img alt="login" src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9uZXdiZWUtbWFsbC5vc3MtY24tYmVpamluZy5hbGl5dW5jcy5jb20vcG9zdGVyL3Byb2R1Y3QvbWFuYWdlLWxvZ2luLnBuZw?x-oss-process=image/format,png" /> </li> <li> 商品管理<br /> <img alt="goods" src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9uZXdiZWUtbWFsbC5vc3MtY24tYmVpamluZy5hbGl5dW5jcy5jb20vcG9zdGVyL3Byb2R1Y3QvbWFuYWdlLWdvb2RzLnBuZw?x-oss-process=image/format,png" /> </li> <li> 商品编辑<br /> <img alt="" src="https://img-bss.csdnimg.cn/202103050348242799.png" /> </li> </ul> </div> </div> </div> </div>
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值