新普京网站-澳门新普京 > 前端 > 编程风格指南

编程风格指南

2019/12/30 04:15

讲授即使写起来很忧伤, 但对保险代码可读性至关心重视要. 上面包车型地铁平整描述了怎么解说甚至在何地注释. 当然也要切记: 注释尽管很要紧, 但最棒的代码本人应当是自文书档案化. 有意义的门类名和变量名, 要远超过要用注释解释的含糊不清的名字.

2.1. 名字空间

鼓励在 .cc 文件Nelly用无名氏名字空间. 使用具名的名字空间时, 其名称可依照项目名或相对路线. 幸免选拔 using 提醒(using-directive)。禁用内联命名空间(inline namespace)。

新普京网站 ,定义:

名字空间将全局意义域细分为独立的, 签名的效用域, 可有效防守全局功用域的命名冲突.

优点:

虽说类已经提供了(可嵌套的)命名轴线 (尤尔Fox 注: 将命名分割在不一致类的效应域内State of Qatar, 名字空间在此底蕴上又包装了大器晚成层.

譬释迦牟尼佛讲来讲, 七个例外类型的全局作用域都有三个类 Foo, 那样在编写翻译或运营时产生冲突. 倘使种种连串将代码置于不一样名字空间中, project1::Foo澳门新普京 , 和 project2::Foo 作为差别符号自然不会冲突.

内联命名空间会活动把里面包车型大巴标记符放到外围作用域,举例:

namespace X {
inline namespace Y {
void foo();
}
}

X::Y::foo() 与 X::foo() 互相可代表。内联命名空间最主要用以保险跨版本的 ABI 包容性。

缺点:

名字空间有所吸引性, 因为它们和类雷同提供了额外的 (可嵌套的卡塔尔(قطر‎ 命名轴线.

取名空间比较轻便令人迷惑,毕竟它们不再受其声称所在命名空间的约束。内联命名空间只在大型版本调控里有用。

在头文件中动用佚名空间招致违背 C++ 的唯一定义法则 (One Definition Rule (OD奥迪Q7卡塔尔国卡塔尔.

结论:

基于下文就要提到的国策合理接收命名空间.

你写的申明是给代码读者看的: 下多少个内需精晓您的代码的人. 慷慨些吗, 下一人也许正是你!

2.1.1. 无名名字空间

在 .cc 文件中, 允许以至勉力利用无名名字空间, 以幸免运维时的命名矛盾:

namespace {                             // .cc 文件中

// 名字空间的内容无需缩进
enum { kUNUSED, kEOF, kERROR };         // 经常使用的符号
bool AtEof() { return pos_ == kEOF; }   // 使用本名字空间内的符号 EOF

} // namespace

唯独, 与一定类关联的文件效率域注明在这里类中被声称为品种, 静态数据成员或静态成员函数, 并不是无名名字空间的成员. 如上例所示, 佚名空间甘休时用注释 // namespace 标识.

不要在 .h 文件中利用无名氏名字空间.

7.1. 讲明风格

使用 // 或 /* */, 统生龙活虎就好.

// 或 /* */ 都可以; 但 //  常用. 要在如何疏解及注释风格上保险统意气风发.

2.1.2. 签名的名字空间

签订公约的名字空间应用方法如下:
用名字空间把手拿包含, gflags 的扬言/定义, 以至类的放置申明以外的满贯源文件封装起来, 以分别于别的名字空间:

// .h 文件
namespace mynamespace {

// 所有声明都置于命名空间中
// 注意不要使用缩进
class MyClass {
    public:
    …
    void Foo();
};

} // namespace mynamespace

// .cc 文件
namespace mynamespace {

// 函数定义都置于命名空间中
void MyClass::Foo() {
    …
}

} // namespace mynamespace

通常的 .cc 文件富含更加多, 更复杂的细节, 譬如引用其余名字空间的类等.

#include “a.h”

DEFINE_bool(someflag, false, “dummy flag”);

class C;                    // 全局名字空间中类 C 的前置声明
namespace a { class A; }    // a::A 的前置声明

namespace b {

…code for b…                // b 中的代码

} // namespace b

永不在名字空间 std 内评释任李继宏西, 富含规范库的类前置注明. 在 std 名字空间阐明实体会促成不鲜明的难题, 比方不可移植. 申明规范库下的实业, 需求富含相应的头文件.

极端不用选取 using 提示,以管教名字空间下的持有名称都足以正常使用.

// 禁止 —— 污染名字空间
using namespace foo;

在 .cc 文件, .h 文件的函数, 方法或类中, 能够运用 using 评释。

// 允许: .cc 文件中
// .h 文件的话, 必须在函数, 方法或类的内部使用
using ::foo::bar;

在 .cc 文件, .h 文件的函数, 方法或类中, 允许使用名字空间外号.

// 允许: .cc 文件中
// .h 文件的话, 必须在函数, 方法或类的内部使用

namespace fbz = ::foo::bar::baz;

// 在 .h 文件里
namespace librarian {
//以下别名在所有包含了该头文件的文件中生效。
namespace pd_s = ::pipeline_diagnostics::sidetable;

inline void my_inline_function() {
  // namespace alias local to a function (or method).
  namespace fbz = ::foo::bar::baz;
  ...
}
}  // namespace librarian

细心在 .h 文件的外号对含蓄了该头文件的全体人可知,所以在公共头文件(在品种外可用)以致它们递归包含的此外头文件里,不要用别称。终究原则上共用 API 要尽量地精短。

幸免用内联命名空间

7.2. 文本注释

在每贰个文书开端参加版权通知, 然后是文件内容描述.

准则布告和作者消息:

每种文件都应有富含以下项, 依次是:

  • 版权注解 (举个例子, Copyright 2008 Google Inc.)
  • 执照. 为品种接纳适当的许可证版本 (举例, Apache 2.0, BSD, LGPL, GPL卡塔尔(قطر‎
  • 笔者: 标记文件的原始作者.

假设你对原始小编的文本做了举足轻重改正, 将您的音信增加到笔者新闻里. 那样当其余人对该文件有问号时方可清楚该联系何人.

文件内容:

紧接着版权许可和小编音讯之后, 种种文件都要用注释描述文件内容.

通常, .h 文件要对所注脚的类的功力和用法作简单表达. .cc 文件平日富含了越多的落到实处细节或算法手艺探讨, 假诺你认为那些达成细节或算法技能钻探对于明白 .h 文件有救助, 能够将该注释挪到 .h, 并在 .cc 中建议文书档案在 .h.

永不轻松的在 .h 和 .cc 间复制注释. 这种偏离了讲明的实在乎义.

2.2. 嵌套类

当公有嵌套类作为接口的黄金时代有个别时, 纵然能够直接将他们保持在大局意义域中, 但将嵌套类的扬言置于 2.1. 名字空间 内是越来越好的选择.

概念: 在八个类内部定义另三个类; 嵌套类也被可以称作 成员类 (member class).

class Foo {

private:
    // Bar是嵌套在Foo中的成员类
    class Bar {
        …
    };

};

优点:

当嵌套 (或成员卡塔尔 类只被外部类应用时特别管用; 把它当做外部类功用域内的分子, 而不是去污染外界成效域的同名类. 嵌套类可以在外面类中做前置注明, 然后在 .cc 文件中定义, 那样防止在外围类的注脚中定义嵌套类, 因为嵌套类的概念日常只与完结相关.

缺点:

嵌套类只可以在外围类的当中做前置注明. 因而, 任何利用了 Foo::Bar* 指针的头文件不能不包括类 Foo 的全部注明.

结论:

并非将嵌套类定义成公有, 除非它们是接口的黄金时代部分, 比方, 嵌套类含有有些方法的黄金时代组选项.

7.3. 类注释

种种类的概念都要附带风度翩翩份注释, 描述类的作用和用法.

// Iterates over the contents of a GargantuanTable.  Sample usage:
//    GargantuanTable_Iterator* iter = table->NewIterator();
//    for (iter->Seek("foo"); !iter->done(); iter->Next()) {
//      process(iter->key(), iter->value());
//    }
//    delete iter;
class GargantuanTable_Iterator {
    ...
};

生机勃勃旦你认为已经在文书最上端详细描述了此类, 想一贯省略的来上一句 “完整描述见文件顶端” 也不打紧, 但必得确认保障有那类注释.

设若类有其余协同前提, 文档表达之. 即使此类的实例可被四线程访问, 要非常注意文书档案表达多线程情状下有关的平整和常量使用.

2.3. 非分子函数、静态成员函数和全局函数

动用静态成员函数或名字空间内的非成员函数, 尽量不要用裸的全局函数.

优点:

好几意况下, 非成员函数和静态成员函数是老大管用的, 将非成员函数放在名字空间内可制止污染全局意义域.

缺点:

将非成员函数和静态成员函数作为新类的分子或者更有意义, 当它们必要寻访外界能源或有所首要的依赖性关系时特别如此.

结论:

突发性, 把函数的定义同类的实例脱钩是方便的, 以致是必须的. 那样的函数能够被定义成静态成员, 或是非成员函数. 非成员函数不应信任于外界变量, 应竭尽停放有个别名字空间内. 比较单纯为了封装若干不分享任徐健态数据的静态成员函数而成立类, 不及使用 2.1. 名字空间。

概念在相似编写翻译单元的函数, 被其余编写翻译单成分来调用只怕会引进不要求的耦合和链接时依赖; 静态成员函数对此极度敏感. 能够构思提取到新类中, 也许将函数置于独立库的名字空间内.

假设你必须定义非成员函数, 又只是在 .cc 文件中应用它, 可选取无名氏 namespaces`或 “static` 链接关键字 (如 static int Foo() {...}卡塔尔 限制其效果域.

7.4. 函数注释

函数阐明处注释描述函数功效; 定义处描述函数达成.

函数评释:

讲授坐落于注明在此之前, 对函数功效及用法实行描述. 注释使用叙述式 (“Opens the file”State of Qatar 而非指令式 (“Open the file”State of Qatar; 注释只是为了描述函数, 并不是命令函数做怎么样. 平常, 注释不会汇报函数如何专门的学业. 那是函数定义部分的事情.

函数证明处注释的原委:

  • 函数的输入输出.
  • 对类成员函数来讲: 函数调用时期对象是否要求保持引用参数, 是或不是会放出那么些参数.
  • 假使函数分配了上空, 要求由调用者释放.
  • 参数是不是可以为 NULL.
  • 是或不是存在函数使用上的习性隐患.
  • 如果函数是可重入的, 其一齐前提是何许?

比方如下:

// Returns an iterator for this table.  It is the client's
// responsibility to delete the iterator when it is done with it,
// and it must not use the iterator once the GargantuanTable object
// on which the iterator was created has been deleted.
//
// The iterator is initially positioned at the beginning of the table.
//
// This method is equivalent to:
//    Iterator* iter = table->NewIterator();
//    iter->Seek("");
//    return iter;
// If you are going to immediately seek to another place in the
// returned iterator, it will be faster to use NewIterator()
// and avoid the extra seek.
Iterator* GetIterator() const;

但也要幸免罗罗嗦嗦, 或做些总体上看的表明. 上边包车型客车注释就从未有过供给加上 “returns false otherwise”, 因为早就满含此中了:

// Returns true if the table cannot hold any more entries.
bool IsTableFull();

讲解布局/析构函数时, 切记读代码的人知情愫构/析构函数是干啥的, 所以 “destroys this object” 那样的解说是未曾意思的. 申明布局函数对参数做了怎样 (比方, 是还是不是获得指针全数权卡塔尔以致析构函数清理了哪些. 假设都以些不在乎的剧情, 直接省掉注释. 析构函数前不曾注释是很正规的.

函数定义:

每种函数定义时要用注释表明函数功效和达成要点. 比方说说您用的编制程序本领, 实现的大致步骤, 或表明如此完成的说辞, 为啥前半局地要加锁而后半局地不需求.

不要 从 .h 文件或其它地点的函数证明处直接复制注释. 简要重述函数功能是足以的, 但注释注重要放在什么完结上.

2.4. 有的变量

将函数变量尽只怕置于最小功用域内, 并在变量表明时开展早先化.

C++ 允许在函数的其他地方注脚变量. 大家提倡在尽量小的功能域中宣示变量, 离第一遍选择越近越好. 那使得代码浏览者更便于定位变量申明的职位, 驾驭变量的连串和开头值. 特别是,应使用开首化的秘籍替代表明再赋值, 举个例子:

int i;
i = f(); // 坏——初始化和声明分离
int j = g(); // 好——初始化时声明

vector<int> v;
v.push_back(1); // 用花括号初始化更好
v.push_back(2);

vector<int> v = {1, 2}; // 好——v 一开始就初始化

留意, GCC 可正确落实了 for (int i = 0; i < 10; ++i) (i 的功用域只限 for 循环内卡塔尔(قطر‎, 所以别的 for巡回中得以另行行使 i. 在 if 和 while 等语句中的功能域注明也是未可厚非的, 如:

while (const char* p = strchr(str, ‘/’)) str = p + 1;

Warning

即便变量是一个指标, 每一遍进入效率域都要调用其布局函数, 每便退出功用域都要调用其析构函数.

// 低效的实现
for (int i = 0; i < 1000000; ++i) {
    Foo f;                  // 构造函数和析构函数分别调用 1000000 次!
    f.DoSomething(i);
}

在循环效用域外面声明那类变量要高速的多:

Foo f;                      // 构造函数和析构函数只调用 1 次
for (int i = 0; i < 1000000; ++i) {
    f.DoSomething(i);
}

7.5. 变量注释

平时变量名本身能够很好表明变量用场. 某个情形下, 也亟需十三分的注释表明.

类数据成员:

各种类数据成员 (也叫实例变量或成员变量) 都应该用注释表达用项. 如若变量能够担负 NULL 或 -1 等警示值, 须加以表达. 举例:

private:
    // Keeps track of the total number of entries in the table.
    // Used to ensure we do not go over the limit. -1 means
    // that we don't yet know how many entries the table has.
    int num_total_entries_;

全局变量:

和数目成员风度翩翩致, 全数全局变量也要讲明表明含义及用处. 比方:

// The total number of tests cases that we run through in this regression test.
const int kNumTestCases = 6;

2.5. 静态和全局变量

取缔接纳 class 类型的静态或全局变量:它们会促成难以开掘的 bug 和不分明的结构和析构函数调用顺序。可是 constexpr 变量除却,毕竟它们又不关乎动态初叶化或析构。

静态生存周期的靶子,即满含了全局变量,静态变量,静态类成员变量和函数静态变量,都一定要是原生数据类型 (POD : Plain Old Data卡塔尔: 即 int, char 和 float, 以致 POD 类型的指针、数组和布局体。

静态变量的布局函数、析构函数和伊始化的顺序在 C++ 中是不明确的,以至随着创设转变而变化,招致难以觉察的 bug. 所以除了禁止使用类类型的全局变量,大家也不容许用函数再次回到值来初叶化 POD 变量,除非该函数不涉及(举个例子 getenv(卡塔尔(قطر‎ 或 getpid(卡塔尔)不关乎别的全局变量。(函数成效域里的静态变量除却,究竟它的早先化顺序是有明显概念的,何况只会在命令推行到它的宣示这里才会生出。)

同理,全局和静态变量在程序中断时会被析构,无论所谓中断是从 main() 再次回到还是对 exit() 的调用。析构顺序偏巧与构造函数调用的顺序相反。但既然布局顺序未定义,那么析构顺序当然也就不定了。比如,在程序结束时某静态变量已经被析构了,但代码还在跑——举例其余线程——并思考访谈它且失利;再举个例子,一个静态 string 变量只怕会在一个引用了后面一个的此外变量析构以前被析构掉。

改良以上析构难题的不二法门之一是用 quick_exit() 来代替 exit() 并中断程序。它们的不一样之处是前者不会进行别的析构,也不会施行 atexit() 所绑定的别的handlers. 借使你想在推行 quick_exit()来行车制动器踏板时实行某 handler(例如刷新 log),您能够把它绑定到 _at_quick_exit(). 要是您想在 exit()和 quick_exit() 都用上该 handler, 都绑定上去。

归咎,我们只允许 POD 类型的静态变量,即完全禁用 vector (使用 C 数组代替卡塔尔(قطر‎ 和 string (使用 const char [])。

设若您确实须求三个 class 类型的静态或全局变量,能够虚构在 main() 函数或 pthread_once() 内初阶化三个指南针且毫无回笼。注意只好用 raw 指针,别用智能指针,终究前者的析构函数涉及到上文建议的骚动顺序难题。

Yang.Y 译注:

上文聊到的静态变量泛指静态生存周期的指标, 满含: 全局变量, 静态变量, 静态类成员变量, 以致函数静态变量.

上一篇:没有了 下一篇:没有了