关于c ++:改进代码的准则

关于c ++:改进代码的准则

Guidelines to improve your code

您遵循什么准则来提高代码的总体质量?许多人都有关于如何编写C ++代码的规则,这些规则(据说)会使出错更加困难。我见过人们坚持认为,每个if语句后都应有一个大括号({...})。

我对其他人遵循的准则以及背后的原因感兴趣。我对您认为是垃圾但普遍使用的准则也很感兴趣。有人可以建议一些吗?

为了使事情顺利进行,我将首先介绍一些:

  • 始终在每个if / else语句之后使用大括号(如上所述)。这样做的基本原理是,要说一个语句实际上是一个语句还是一个扩展到多个语句的预处理器宏并不总是很容易,所以这段代码会中断:
1
2
3
4
5
6
    // top of file:
    #define statement doSomething(); doSomethingElse

    // in implementation:
    if (somecondition)
        doSomething();

但是,如果使用括号,它将按预期工作。

  • 仅将预处理器宏用于条件编译。预处理程序宏不允许C ++作用域规则,因此可能会引起各种麻烦。由于预处理器宏在头文件中具有通用名称,因此我已经搁浅了很多次。如果不小心,可能会造成各种破坏!

现在交给您。


我个人的一些最爱:

努力编写const正确的代码。您将邀请编译器帮助您清除容易修复但有时很痛苦的错误。您的代码还将讲述您编写代码时的想法-一旦您离开,对于新手或维护者来说都是有价值的。

退出内存管理业务。学习使用智能指针:std::auto_ptrstd::tr1::shared_ptr(或boost::shared_ptr)和boost::scoped_ptr。了解它们之间的区别以及何时使用一个与另一个。

您可能会使用标准模板库。阅读Josuttis书。不要仅仅在关于容器的前几章之后停下来,以为您知道STL。深入研究好东西:算法和函数对象。


  • 删除不必要的代码。
  • 就这些。


    打开您可以在编译器中看到的所有警告(gcc:-Wall是一个很好的开始,但不包含所有内容,因此请检查文档),并使其出错,因此您必须对其进行修复(gcc:-Werror) 。


    • 使用并执行通用的编码风格和准则。基本原理:团队或公司中的每个开发人员都可以阅读代码,而不会因花括号样式或相似样式而产生干扰。
    • 定期对整个源库进行全面重建(即每天进行构建或在每次签入后进行构建)并报告任何错误!基本原理:来源几乎总是处于可用状态,并且在"实施"问题后不久就发现了问题,而解决问题的成本很低。

    其中一个答案中提到的Google风格指南非常扎实。其中有一些毫无意义的东西,但它总有好有坏。

    Sutter和Alexandrescu在这方面写了一本不错的书,叫做C ++编码标准。

    这里有一些来自lil'ole me的一般提示:

  • 您的缩进和包围样式都是错误的。其他人也一样。因此,请遵循该项目的标准。吞下自豪感并设置您的编辑器,以使所有内容与其余代码库尽可能保持一致。不得不阅读不一致缩进的代码真的很烦人。就是说,括号和缩进与"改进代码"没有任何关系。这更多地是关于提高与他人合作的能力。

  • 好的评论。这是非常主观的,但是总的来说,写注释说明代码为什么会按其方式工作总是好事,而不是解释其工作方式。当然,对于复杂的代码,对可能不熟悉算法或代码的程序员也有一个很好的想法也很有益。非常欢迎链接到所用算法的描述。

  • 以尽可能简单的方式表达逻辑。具有讽刺意味的是,诸如"在比较左侧放置常量"之类的建议在这里出错了。它们非常受欢迎,但是对于讲英语的人,他们经常破坏程序对那些阅读者的逻辑流程。如果您不信任自己(或您的编译器)正确编写相等比较,那么请务必使用此类技巧。但是,这样做时您会牺牲清晰度。诸如此类的事情也属于这一类:"我的逻辑是否具有3个缩进级别?会更简单吗?"并将类似的代码滚动到函数中。甚至拆分功能。编写优雅地表达底层逻辑的代码需要经验,但值得一试。

  • 那些很一般。对于特定的提示,我无法比Sutter和Alexandrescu做得更好。


    在if语句中,将常量放在左侧,即

    1
    if( 12 == var )

    1
    if( var == 12 )

    因为如果您错过键入" =",那么它将成为赋值。在最高版本中,编译器说这是不可能的,在后者中它会运行并且if始终为true。

    每当它们不在同一行时,我都会用大括号表示。

    1
    2
    3
    4
    5
    if( a == b ) something();
    if( b == d )
    {
        bigLongStringOfStuffThatWontFitOnASingleLineNeatly();
    }

    打开和关闭大括号总是有自己的特点。但这当然是个人习惯。


    仅在仅需要解释代码的功能时才发表评论,而在阅读代码的过程中并不能告诉您相同的内容。

    不要注释掉不再使用的代码。如果要恢复旧代码,请使用源代码控制系统。注释掉代码只会使事情看起来混乱,并使实际上很重要的注释逐渐淡入注释代码的背景。


    Google内部还使用了不错的C ++样式指南,其中包括此处提到的大多数规则。


  • 使用一致的格式。
  • 在处理遗留代码时,请使用现有的格式设置样式,尤其是。大括号样式。
  • 获得Scott Meyer的书《有效C ++》
  • 获取Steve MConnell的书Code Complete的副本。

  • 开始写很多评论-但要以此为契机重构代码,以使其易于说明。

    即:

    1
    2
    3
    for(int i=0; i<=arr.length; i++) {
      arr[i].conf() //confirm that every username doesn't contain invalid characters
    }

    应该更像

    1
    2
    3
    for(int i=0; i<=activeusers.length; i++) {
      activeusers[i].UsernameStripInvalidChars()
    }

    同样,您可能会在这里找到一些有用的建议:如何使错误的代码看起来不正确?您使用什么模式来避免语义错误?


    • 使用制表符进行缩进,但将数据与空格对齐
      这意味着人们可以通过更改制表符的大小来决定缩进多少,而且还可以保持对齐(例如,在将值分配给结构时,您可能希望所有'='都位于垂直线内)

    • 总是在可能的地方使用常量或内联函数代替宏

    • 切勿在头文件中使用" using",因为包括头文件在内的所有内容也会受到影响,即使包含头文件的人不希望其全局命名空间中的所有std(例如)也是如此。

    • 如果某事物的长度超过约80种,则将其分成多行,例如

      1
      2
      3
      4
      5
      if(SomeVeryLongVaribleName != LongFunction(AnotherVarible, AString) &&
         BigVaribleIsValid(SomeVeryLongVaribleName))
      {
          DoSomething();
      }
    • 只有重载运算符才能使它们执行用户期望的操作,例如重载2dVector的+和-运算符就可以了

    • 始终注释您的代码,即使只是说出下一个块在做什么(例如,"删除此级别不需要的所有纹理")。有人可能稍后需要使用它,可能是在您离开后,他们不想找到没有注释的1000行代码来表明正在做什么。


  • 设置编码约定并使所有相关人员遵循该约定(您不希望阅读要求您弄清楚下一条语句/表达式在哪里的代码,因为它没有正确缩进)
  • 不断地重构代码(获得Martin Fowler撰写的《重构》的副本,本书中详细介绍了利弊)
  • 编写松散耦合的代码(避免通过编写不言自明的代码来写注释,松散耦合的代码往往更易于管理/易于更改)
  • 如果可能,请对您的代码进行单元测试(或者,如果您有足够的能力,则为TDD)
  • 提前发布,经常发布
  • 避免过早优化(分析有助于优化)

  • 六个月后再看


    如果可以,请使用前增量而不是后增量。


    我在C ++项目上使用PC-Lint,尤其喜欢它引用现有出版物的方式,例如MISRA准则或Scott Meyers的"有效C ++"和"更有效的C ++"。即使您打算为静态分析工具检查的每个规则编写非常详细的说明,也要指出用户信任的已建立出版物是一个好主意。


    这是我由C ++专家提供的最重要的建议,它在一些关键情况下帮助我在代码中查找错误:

    • 当不应该使用const方法修改对象时,请使用const方法。
    • 当不应修改对象时,请在参数中使用const引用和指针。

    使用这两个规则,编译器将免费告诉您代码中逻辑存在缺陷的地方!


    另外,对于某些好的技巧,您可以关注Google的博客"马桶上的测试"。


    确保您正确缩进


    智能指针具有一种非常清晰地指示所有权的好方法。如果您是类或函数:

    • 如果您得到了原始指针,那么您什么都不拥有。允许您使用呼叫者,由您的呼叫者提供,呼叫者保证呼叫者的存活时间比您长。
    • 如果您获得了weak_ptr,则说明您没有该指针,并且该指针可以随时消失。
    • 如果您获得了shared_ptr,则您将与其他对象一起拥有该对象,因此无需担心。减轻压力,但也减少控制。
    • 如果获得auto_ptr,则表明您是该对象的唯一所有者。是你的,你是国王。您有权破坏该对象或将其交给其他人(从而失去所有权)。

    我发现auto_ptr的情况特别强烈:在设计中,如果看到auto_ptr,我立即知道该对象将从系统的一部分"漫游"到另一部分。

    至少这是我在宠物项目中使用的逻辑。我不确定该主题可以有多少种变化,但是直到现在,这个规则集对我还是很有帮助。


    嗯-我可能应该更具体一些。

    我并不是在为自己寻找建议-我正在编写一个静态代码分析工具(当前的商业产品还不足以满足我的需求),我正在寻找插件的想法以突出可能的方法代码中的错误。

    一些人提到了const正确性和使用智能指针之类的东西-我可以检查这种想法。检查缩进和注释有点困难(无论从编程角度来看)。


    推荐阅读