写在前面:最新公司马上就要开始开发一款视觉产品,工程量较大,且需要对客户提供可以二次开

发的SDK,整个项目用C++编写。

这就对代码质量提出了非常高的要求,同时,如何设计出优雅稳定的API也是相当大的挑战。

当然,团队首先需要解决的问题是编程规范的确立。之前,公司规模较小,对C++代码规范不够重视,导致各个C++项目代码质量参差不齐,标准不一。

所以,公司领导希望借此视觉项目的机会,建立起公司C++编码规范,统一指导后续c++开发项目。建立编码规范本身也是对C++语言的再学习过程,利于提高整个团队对C++的理解和使用。

经过简单调研,我们决定使用Google C++ Coding Style。作为项目主要开发人员,现将学习的心得记录如下,注意,这里不是对Google C++ Coding Style的全文翻译,而是捕捉和记录一些可能遗漏的要点。

1、Headers

1.1 self-contained headers

Header files should be self-contained (compile on their own)

Prefer placing the definitions for template and inline functions in the same file as their declarations. The definitions of these constructs must be included into every .cc file that uses them, or the program may fail to link in some build configurations.

As an exception, a template that is explicitly instantiated for all relevant sets of template arguments, or that is a private implementation detail of a class, is allowed to be defined in the one and only .cc file that instantiates the template.

(推荐将模板类和内敛函数的声明和定义放在一个头文件里面,为什么?下面的文章可以说明。

When the compiler encounters a declaration of a TestTemp object of some specific type, e.g., int, it must have access to the template implementation source. Otherwise, it will have no idea how to construct the TestTemp member functions. And, if you have put the implementation in a source (TestTemp.cpp) file and made it a separate part of the project, the compiler will not be able to find it when it is trying to compile the client source file. And, #includeing the header file (TestTemp.h) will not be sufficient at that time. That only tells the compiler how to allocate for the object data and how to build the calls to the member functions, not how to build the member functions. And again, the compiler won't complain. It will assume that these functions are provided elsewhere, and leave it to the linker to find them. So, when it's time to link, you will get "unresolved references" to any of the class member functions that are not defined "inline" in the class definition.)

1.2 The #define guard

<PROJECT>_<PATH>_<FILE>_H_

(主要是命名格式问题)

1.3 Forward Declaration

Avoid using forward declarations where possible. Just #include the headers you need.

(尽量避免使用前置声明,虽然前置声明可以减少编译时间,避免不需要的重编译,但是还是可能会规避掉必要的重新编译,无法在后续对头文件API进行更改,还会隐藏类与类之间的继承关系导致多态失效等等,总之,不要用前置声明)

1.4 Inline Functions

Define functions inline only when they are small, say, 10 lines or fewer.

Another useful rule of thumb: it's typically not cost effective to inline functions with loops or switch statements

(只在代码行数少于10行的情况下使用内联函数,递归函数不要使用内联函数,函数内有循环或者switch不要使用内联函数,虚函数可以使用内敛,但是更多是为了方便或者生成文档,虽然编译器不一定会将它作为内联函数处理)

1.5 Names and Orders of Includes

  1. dir2/foo2.h.
  2. A blank line
  3. C system files.
  4. C++ system files.
  5. A blank line
  6. Other libraries' .h files.
  7. Your project's .h files.

(头文件需要放在project文件名下,即project/dir1/a.h,不要使用UNIX规范下的./..等相对路径。

至于为什么要把该实现文件的头文件放在第一个,原因如下:

Including the .h file as the very first line of the .c file ensures that no critical piece of information intrinsic to the physical interface of the component is missing from the .h file (or, if there is, that you will find out about it as soon as you try to compile the .c file)

它能确保头文件包含的内容完整,这样,如果头文件有问题,那么实现这个类的人将首先碰到编译问题,而不是留给其他使用这个类的人。记住,不要指望通过include某个头文件来顺带引入另个需要的头文件,之前说过,每个头文件都要保持self-contained,直接引入你需要的那个header就好。)

2. Scoping

2.1 NameSpace

名称空间主要是用来避免名称冲突。有几点需要注意的,一是不要往std空间引入其他任何的名称,二是不要通过使用using来引入一个名称空间下所有的名称,但你可以引入某个具体的名称,三是如果要对名称空间使用别名,请在局部名称空间或局部函数,即局部作用域使用别名,不要在顶级空间使用别名,否则将会成为对外公开API的一部分,最后就是不要使用内联名称空间。

2.2 Unamed namespaces and Static variables

未命名的名称空间和静态变量声明可以限制变量或者函数的作用域为本文件,这样就可以在不同的文件中的最外层作用域定义同名变量而不会出现名称冲突,不需要外部链接的函数或者变量都可以放到未命名名称空间里面。internal link的解释可以参见https://stackoverflow.com/questions/1358400/what-is-external-linkage-and-internal-linkage

2.3 Nonmember, static member and Global functions

非成员函数放到名称空间里面,少用全局函数。不要将所有的静态方法集中组合到一个单独的类中,说白了,哪怕是静态方法,你也得放在一个与之相关的普通的类中,静态不代表就应该全部独立出来。如果只需要本文件可见,放入未命名名称空间里。

2.4 Local variables

局部变量尽量在靠近第一次使用的地方声明,并及时初始化。

2.4 Static and Global variables

这个问题相对比较复杂,涉及到很多c++11&14的新特性新概念。

首先关于什么是trivally destructable: More formally it means that the type has no user-defined or virtual destructor and that all bases and non-static members are trivially destructible. 说白了,就是没有复杂的析构函数

All objects with static storage duration are destroyed at program exit (which happens before unjoined threads are terminated. )

关于多线程的情形,静态变量的生存周期结束的时刻是要早于unjoined线程被结束的时刻的。也就是说,unjoined线程可能访问到一个已经被析构的静态对象从而导致出错。

静态对象的初始化分为动态初始化和静态初始化,静态初始化即我们常说的自动置零,而动态初始化则涉及其他的操作,例如调用一个构造函数等,相对复杂。不同translation unit中的静态对象的生存周期的起始点没有一定先后顺序。所以就可能导致引用一个尚未初始化的静态对象。析构的顺序同样不能保证,unjoined线程最终可能试图访问一个已经被析构的静态对象。

所以,只有对象是trivally-destructible的时候,才能被用作全局或静态,因为trivally-desctructible不受析构的顺序的影响。constexpr因为是编译期确定,所以也算作简单可析构。

从初始化角度来看,只要不是常量初始化(constant initialization)那么其初始化顺序就无法保证,因此 Dynamic initialization of nonlocal variables is discouraged, and in general it is forbidden. 除非保证该变量的初始化与其他变量的初始化顺序无关(或者说其他静态变量的初始化不会引用到这个变量)

局部静态变量的动态初始化是OK的,不受上述规则影响。

啰里啰嗦说了那么多,总结起来就是,静态变量初始和析构顺序不确定,所以,对于这类变量,只用常量初始化,解除对顺序的依赖。析构也保证要trival。

2.5 thread-local variables

局部线程变量的生存期类似于静态变量,但是每个线程都有一个独立的拷贝,变量生成于线程创建,卒于线程结束。

类似于静态变量,局部线程变量也会面临初始化顺序问题(除非在函数体内声明),但不会面临析构顺序问题。

如果要在外层声明,请对变量进行常量初始化。

(未完待续……)

Google C++ Coding Style 学习笔记的更多相关文章

  1. Google Guava14.0 瓜娃学习笔记

    Guava 是java api的增强与扩展,提供复杂的java 数据结构,使你的代码更简短精炼,具有良好的可读性.看看guava给我们提供了哪些很酷的功能: 集合创建: Map<String, ...

  2. python coding style guide 的高速落地实践

    python coding style guide 的高速落地实践 机器和人各有所长,如coding style检查这样的可自己主动化的工作理应交给机器去完毕,故发此文帮助你在几分钟内实现coding ...

  3. python coding style guide 的快速落地实践——业内python 编码风格就pep8和谷歌可以认作标准

    python coding style guide 的快速落地实践 机器和人各有所长,如coding style检查这种可自动化的工作理应交给机器去完成,故发此文帮助你在几分钟内实现coding st ...

  4. Google's C++ coding style

    v0.2 - Last updated November 8, 2013 源自 Google's C++ coding style rev. 3.274 目录 由 DocToc生成     头文件   ...

  5. 学习笔记之Coding / Design / Tool

    CODING 学习笔记之代码大全2 - 浩然119 - 博客园 https://www.cnblogs.com/pegasus923/p/5301123.html 学习笔记之编程珠玑 Programm ...

  6. Google TensorFlow深度学习笔记

    Google Deep Learning Notes Google 深度学习笔记 由于谷歌机器学习教程更新太慢,所以一边学习Deep Learning教程,经常总结是个好习惯,笔记目录奉上. Gith ...

  7. c coding style之学习篇

    1. 使用do-while结构去避免潜在的内存泄漏问题. do {     p1 = malloc(10);     if (null == p1)     {         break;     ...

  8. vue学习笔记(三)class和style绑定

    前言 通过上一章的学习vue学习笔记(二)vue的生命周期和钩子函数,我们已经更近一步的知道了关于vue的一些知识,本篇博客将进一步探讨vue其它方面的内容,vue中关于class和style绑定,关 ...

  9. Google coding Style Guide : Google 编码风格/代码风格 手册/指南

    1 1 1 https://github.com/google/styleguide Google 编码风格/代码风格 手册/指南 Style guides for Google-originated ...

随机推荐

  1. 去除List中的重复元素

    /** * 去重list中的重复元素 * @param list * @return */ public static <T> List<T> removeRepeat(Lis ...

  2. 【BZOJ】4709: [Jsoi2011]柠檬

    4709: [Jsoi2011]柠檬 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 779  Solved: 310[Submit][Status][ ...

  3. 矩阵乘法快速幂 cojs 1717. 数学序列

    矩阵乘法模板: #define N 801 #include<iostream> using namespace std; #include<cstdio> int a[N][ ...

  4. [转]使用popBackStack()清除Fragment

    Clear back stack using fragments up vote88down votefavorite 27 I ported my Android app to honeycomb ...

  5. Android SDK最小需求

    As a minimum when setting up the Android SDK, you should download the latest tools and Android platf ...

  6. 【张宴】PHP在金山游戏运营中的应用

    PPT下载地址1(国外服务器):http://blog.s135.com/attachment/201105/2011phptc_zy.zip PPT下载地址2(国内服务器):http://ishar ...

  7. POJ 1293 - Duty Free Shop 01背包记录所选物品

    裸的01背包.dp[x]只要是bool型记录当前空间是否可用.. 而为了找到用了哪些物品..dp[x]设置为int型..进行记录.. Program: #include<iostream> ...

  8. Linux下OOM Killer机制详解

    http://www.cnblogs.com/ylqmf/archive/2012/11/05/2754795.html http://wuquan-1230.blog.163.com/blog/st ...

  9. OpenERP实施记录(10):采购补货

    本文是<OpenERP实施记录>系列文章的一部分. 上文中业务部门接到沃尔玛三台联想Y400N笔记本电脑的订单,但是仓库无货.本文需要完成采购补货处理. 1. 联想YN400N是ABC公司 ...

  10. Spring MVC一事务控制问题

    在近期一个项目中用了Spring MVC作为控制层框架,但却出现了一个让人非常费解的问题:事务控制. Spring MVC的配置文件名称为:springMVC-servlet.xml,内容例如以下: ...