• 原则
    • 好代码的原则
    • 类和函数设计指导原则
    • 保证静态类型安全
    • 遵循C++ISO标准
    • 优先编译时检查错误
    • 使用命名空间来限定作用域
    • 优先使用C++特性而不是C特性

    原则

    好代码的原则

    我们参考Kent Beck的简单设计四原则来指导我们的如何写出优秀的代码,如何有效地判断我们的代码是优秀的。

    • 通过所有测试(Passes its tests)
    • 尽可能消除重复 (Minimizes duplication)
    • 尽可能清晰表达 (Maximizes clarity)
    • 更少代码元素 (Has fewer elements)
    • 以上四个原则的重要程度依次降低。这组定义被称做简单设计原则。第一条强调的是外部需求,这是代码实现最重要的;第二点就是代码的模块架构设计,保证代码的正交性,保证代码更容易修改;第三点是代码的可阅读性,保证代码是容易阅读的;最后一点才是保证代码是简洁的,在简洁和表达力之间,我们更看重表达力。

    类和函数设计指导原则

    C++是典型的面向对象编程语言,软件工程界已经有很多OOP原则来指导我们编写大规模的,高可扩展的,可维护性的代码:

    • 高内聚,低耦合的基本原则
    • SOLID原则
    • 迪米特法则
    • “Tell,Don’t ask”原则
    • 组合/聚合复用原则

    保证静态类型安全

    我们希望C++应该是静态类型安全的,这样可以减少运行时的错误,提高代码的健壮性。但是由于C++的下面的特性存在,会破坏C++静态类型安全,我们针对这部分特性要仔细处理。

    • unions联合体
    • 类型转换cast
    • 缩窄转换narrowing conversions
    • 类型退化type decay
    • 范围错误range errors
    • void*类型指针我们可以通过约束这些特性的使用,或者使用C++的新特性,比如variant(C++17),GSL的span,narrow_cast等来解决这些问题,提高C++代码的健壮性。

    遵循C++ISO标准

    希望通过使用ISO C++标准的特性来编写C++代码,对于ISO标准中未定义的或者编译器实现的特性要谨慎使用,对于GCC等编译器的提供的扩展特性也需要谨慎使用,这些特性会导致代码的可移植性比较差。

    注意:如果模块中需要使用相关的扩展特性来,那么尽可能将这些特性封装成独立的接口,并且可以通过编译选项关闭或者编译这些特性。对于这些扩展特性的使用,请模块制定特性编程指南来指导这些特性的使用。

    优先编译时检查错误

    通过编译器来优先保证代码健壮性,而不是通过编写错误处理代码来处理编译就可以发现的异常,比如:

    • 通过const来保证数据的不变性,防止数据被无意修改。
    • 通过gsl::span等来保证char数组不越界,而不是通过运行时的length检查。
    • 通过static_assert来进行编译时检查。

    使用命名空间来限定作用域

    全局变量,全局常量和全局类型定义由于都属于全局作用域,在项目中,使用第三方库中容易出现冲突。

    命名空间将作用域细分为独立的,具名的作用域,可有效地防止全局作用域的命名冲突。

    • class,struct等都具有自己的类作用域。
    • 具名的namespace可以实现类作用域更上层的作用域。
    • 匿名namespace和static可以实现文件作用域。对于没有作用域的宏变量,宏函数强烈建议不使用。

    作用域的一些缺点:

    • 虽然可以通过作用域来区分两个命名相同的类型,但是还是具有迷惑性。
    • 内联命名空间会让命名空间内部的成员摆脱限制,让人迷惑。
    • 通过多重嵌套来定义namespace,会让完整的命名空间比较冗长。所以,我们使用命名空间的建议如下:

    • 对于变量,常量和类型定义尽可能使用namespace,减少全局作用域的冲突

    • 不要在头文件中使用using namespace
    • 不要使用内联命名空间
    • 鼓励在.cpp文件中通过匿名namespace或者static来封装,防止不必要的定义通过API暴露出去。

    优先使用C++特性而不是C特性

    C++比起C语言更加类型安全,更加抽象。我们更推荐使用C++的语言特性来编程,比如使用string而不是char*, 使用vector而不是原生数组,使用namespace而不是static。