Google C++ 编码风格-笔记
来源:互联网 发布:linux流量统计分析 编辑:程序博客网 时间:2024/06/05 00:31
Google C++ 编码风格-笔记
标签(空格分隔): 未分类
官方英文地址
官方中文地址
头文件
1.#define保证
为了防止头文件重复包含。通常用#defined保证,符号是唯一的,格式通常为 <PROJECT>_<PATH>_<FILE>_H_。
#ifndef FOO_SRC_BAR_BAZ_H#define FOO_SRC_BAR_BAZ_H//todo...#endif
2.前向声明
不要用类的前向声明,而是用#include"*.h"的方式。
3.内联函数
1.inline函数不要超过10行;2.inline函数不要含有循环、switch语句。
4.#include的路径及顺序
1.包含文件的名称中不要使用.和..,而是用比较完整的项目路径,看上去清晰;2.源文件#include的文件顺序(以空行分割)依次为:(1)对应头文件;(2)c系统文件;(3)c++系统文件;(4)其他库.h文件(5)本项目.h文件这样可以保证项目的编译错误,最先发现。
作用域
1.使用方法
// In the .h file namespace mynamespace { // All declarations are within the namespace scope. // Notice the lack of indentation. class MyClass { public: ... void Foo() }; } // namespace mynamespace //注释表示结束 // In the .cc file namespace mynamespace { // Definition of functions is within scope of the namespace. void MyClass::Foo() { ... } } // namespace mynamespace
2.命名
1.可使用项目名称或相对路径;
//对于头文件 myproject/foo_bar.h , 应当使用 namespace myproject { namespace foo_bar { void Function1(); void Function2(); } // namespace foo_bar } // namespace myproject
2.禁止使用using指示。滥用using指示会重新导致命名冲突;
using namespace myspaces; //禁止使用该方式
3.禁止使用inline namespace。
3.using声明与using指示
参考:using 声明和using指示的区别
using std::cin;//using声明 using namespace std;//using 指示
using声明一次只能引入一个特定命名空间的成员。而且它的作用域是从声明点开始,直到包含该using声明作用域的末尾,外部作用域的同名实体被屏蔽(但是如果在同一作用域有同名实体,则会出现错误)。实际上一个using声明就如命名空间成员的局部别名一样。
#include"test.h" using namespace std; namespace MySpace{ int i=1; } int i=10; int main() { //using MySpace::i;//正确,覆盖全局变量i=10,使i=1 using namespace MySpace; i++; //错误,二义性 return 0; }
4.匿名namespace和static作用与区别
参加:匿名namespace和static作用与区别
匿名namespace和static都能使定义的全局变量只在当前文件中生效。当时两者实现的方式是有区别的。
namespace{ //匿名namespace...}
区别:
1.static修改的全局变量,它的链接方式变为内部链接,因此不能用来实例化模板;2.匿名namespace为了能够实例化模板,并没有改变链接方式,还是外部链接。它是采用与C++重载的类似的方法,即名字改编来标识唯一的。
5.非成员函数、静态成员函数和全局函数
使用静态成员函数或命名空间内的非成员函数, 尽量不要用裸的全局函数. 将一系列函数直接置于命名空间中,不要用类的静态方法模拟出命名空间的效果,类的静态方法应当和类的实例或静态数据紧密相关.
6.局部变量
尽量置于最小作用域内,并在声明时初始化。
7.静态和全局变量
1.禁止定义静态生存周期的对象非POD 类型(含STL容器).
静态生存周期的对象,即包括了全局变量,静态变量,静态类成员变量和函数静态变量,都必须是原生数据类型 (POD : Plain Old Data): 即 int, char 和 float, 以及 POD 类型的指针、数组和结构体。
2.因为静态变量的构造函数、析构函数、初始化在C++中大部分顺序是不明确的。如果您确实需要一个 class 类型的静态或全局变量,可以考虑在 main() 函数或 pthread_once() 内初始化一个指针且永不回收。注意只能用 raw 指针
类
1.构造函数
参考:为什么不要在构造函数中调用虚函数
不能在构造函数中调用虚函数。因为这种调用,不会重定位到派生类中的虚函数实现,从而导致当前没有子类化实现。
2.隐含类型转换
C++ explicit关键字详解
不要定义隐式类型转换. 对于转换运算符和单参数构造函数, 请使用 explicit 关键字
3.可拷贝类型和可移动类型
1.如果你的类型需要(用户可一眼看出是否可拷贝), 就让它们支持拷贝 / 移动. 否则, 就把隐式产生的拷贝和移动函数禁用.
禁用方法如下:
MyClass(const MyClass&) = delete;MyClass& operator=(const MyClass&) = delete;
2.由于存在对象切割的风险,不要对有派生类的基类定义拷贝构造函数和赋值函数。如果有需求,可以提供一个public virtual clone()和一个protect的拷贝构造函数供派生类实现。
4.结构体 VS. 类
仅当只有数据成员时使用 struct, 其它一概使用 class
5.存取控制
将所有数据成员声明为 private, 除非是 static const 类型成员
函数
1.先输入参数后输出参数.
2.编写简短函数,不要超过40.
3.所有按引用传递的参数必须加const
void Foo(const string &in, string *out);
4.虚函数不可以有缺省参数,缺省参数的值始终保持一致,尽可能使用函数重载.
其他C++特性
1.用 static_cast 替代 C 风格的值转换, 或某个类指针需要明确的向上转换为父类指针时.
2.用 const_cast 去掉 const 限定符.
3.流用来替代 printf() 和 scanf().
4.对于迭代器和其他模板对象使用前缀形式 (++i) 的自增, 自减运算符.
5.强烈建议你在任何可能的情况下都要使用 const
6.使用断言来指出变量为非负数, 而不是使用无符号型
7.使用宏时要非常谨慎, 尽量以内联函数, 枚举和常量代替之.
8.整数用 0, 实数用 0.0, 指针用 nullptr 或 NULL, 字符 (串) 用 ‘\0’.
9.尽可能用 sizeof(varname) 代替 sizeof(type).
Struct data;memset(&data, 0, sizeof(data));
auto
用 auto 绕过烦琐的类型名,只要可读性好就继续用,别用在局部变量之外的地方。
vector<string> v;auto s1 = v[0]; // 创建一份 v[0] 的拷贝。const auto& s2 = v[0]; // s2 是 v[0] 的一个引用。sparse_hash_map<string, int>::iterator iter = m.find(val);//修改为auto iter = m.find(val);//好多了
命名规则
最重要的一致性规则是命名管理
通用命名规则
函数命名, 变量命名, 文件命名要有描述性; 少用缩写.
文件命名
1.文件名要全部小写, 可以包含下划线 () 或连字符 (-), 依照项目的约定. 如果没有约定, 那么 “” 更好.
//可接受的文件命名示例:my_useful_class.ccmy-useful-class.ccmyusefulclass.ccmyusefulclass_test.cc
2.通常应尽量让文件名更加明确. http_server_logs.h 就比 logs.h 要好. 定义类时文件名一般成对出现, 如 foo_bar.h 和 foo_bar.cc, 对应于类 FooBar.
3.内联函数必须放在 .h 文件中
类型命名
1.类型名称的每个单词首字母均大写, 不包含下划线: MyExcitingClass, MyExcitingEnum.
2.所有类型命名 —— 类, 结构体, 类型定义 (typedef), 枚举, 类型模板参数 —— 均使用相同约定, 即以大写字母开始, 每个单词首字母均大写, 不包含下划线.
// 类和结构体class UrlTable { ...class UrlTableTester { ...struct UrlTableProperties { ...// 类型定义typedef hash_map<UrlTableProperties *, string> PropertiesMap;// using 别名using PropertiesMap = hash_map<UrlTableProperties *, string>;// 枚举enum UrlTableErrors { ...
变量命名
变量 (包括函数参数) 和数据成员名一律小写, 单词之间用下划线连接. 类的成员变量以下划线结尾, 但结构体的就不用, 如: a_local_variable, a_struct_data_member, a_class_data_member_.
常量命名(包括静态变量/全局变量)
声明为 constexpr 或 const 的变量, 或在程序运行期间其值始终保持不变的, 命名时以 “k” 开头, 大小写混合. 例如:
const int kDaysInAWeek = 7;
函数命名(适用于类作用域与命名空间作用域的常量)
1.常规函数使用大小写混合: MyExcitingFunction(), MyExcitingMethod()
2.取值和设值函数的命名与变量一致. 一般来说它们的名称与实际的成员变量对应, 但并不强制要求. 例如 int count() 与 void set_count(int count).
命名空间命名
1.命名空间以小写字母命名.
2.建议使用更独特的项目标识符 (websearch::index, websearch::index_util) 而非常见的极易发生冲突的名称 (比如 websearch::util).
3.使用文件名以使得内部名称独一无二 (例如对于 frobber.h, 使用 websearch::index::frobber_internal).
枚举命名
枚举的命名应当和 常量 或 宏 一致: kEnumName 或是 ENUM_NAME.
//常量(首选)enum UrlTableErrors { kOK = 0, kErrorOutOfMemory, kErrorMalformedInput,};//宏方式enum AlternateUrlTableErrors { OK = 0, OUT_OF_MEMORY = 1, MALFORMED_INPUT = 2,};
注释
1.注释风格
使用 // 或 /* */, 统一就好.(自注:对于C++,除了函数定义和文档说明用/**/,其他的用//方式)
2.文件注释
3.类注释
3.函数注释
1.函数声明处注释的内容:
函数的输入输出.
对类成员函数而言: 函数调用期间对象是否需要保持引用参数, 是否会释放这些参数.
函数是否分配了必须由调用者释放的空间.
参数是否可以为空指针.
是否存在函数使用上的性能隐患.
如果函数是可重入的, 其同步前提是什么?
2.函数的定义处
注释重点要放在如何实现上.
4.变量注释
1.特别地, 如果变量可以接受 NULL 或 -1 等警戒值, 须加以说明. 比如:
private: // Used to bounds-check table accesses. -1 means // that we don't yet know how many entries the table has. int num_total_entries_; ``` >2.全局变量 >和数据成员一样, 所有全局变量也要注释说明含义及用途, 以及作为全局变量的原因 ###5.实现注释 >对于代码中巧妙的, 晦涩的, 有趣的, 重要的地方加以注释. ###6.TODO注释 >对那些临时的, 短期的解决方案, 或已经够好但仍不完美的代码使用 TODO 注释. TODO 注释要使用全大写的字符串 TODO, 在随后的圆括号里写上你的名字, 邮件地址, bug ID, 或其它身份标识和与这一 TODO 相关的 issue. 主要目的是让添加注释的人 (也是可以请求提供更多细节的人) 可根据规范的 TODO 格式进行查找. 添加 TODO 注释并不意味着你要自己来修正, 因此当你加上带有姓名的 TODO 时, 一般都是写上自己的名字. ```// TODO(kl@gmail.com): Use a "*" here for concatenation operator.// TODO(Zeke) change this to use relations.// TODO(bug 12345): remove the "Last visitors" feature
如果加 TODO 是为了在 “将来某一天做某事”, 可以附上一个非常明确的时间 “Fix by November 2005”), 或者一个明确的事项 (“Remove this code when all clients can handle XML responses.”).
7.弃用注释
通过弃用注释(DEPRECATED comments)以标记某接口点已弃用.
在 DEPRECATED 一词后, 在括号中留下您的名字, 邮箱地址以及其他身份标识.弃用注释应当包涵简短而清晰的指引, 以帮助其他人修复其调用点. 在 C++ 中, 你可以将一个弃用函数改造成一个内联函数, 这一函数将调用新的接口.
仅仅标记接口为 DEPRECATED 并不会让大家不约而同地弃用, 还得亲自主动修正调用点
格式化
1.空格还是制表位
只使用空格, 每次缩进 2 个空格.我们使用空格缩进. 不要在代码中使用制表符. 你应该设置编辑器将制表符转为空格.
2.函数声明与定义
返回类型和函数名在同一行, 参数也尽量放在同一行, 如果放不下就对形参分行, 分行方式与 函数调用 一致.
ReturnType ClassName::FunctionName(Type par_name1, Type par_name2) { DoSomething(); ...}//如果同一行文本太多, 放不下所有参数:ReturnType ClassName::ReallyLongFunctionName(Type par_name1, Type par_name2, Type par_name3) { DoSomething(); ...}//甚至连第一个参数都放不下:ReturnType LongClassName::ReallyReallyReallyLongFunctionName( Type par_name1, // 4 space indent Type par_name2, Type par_name3) { DoSomething(); // 2 space indent ...}
注意以下几点:
- 使用好的参数名.
- 只有在参数未被使用或者其用途非常明显时, 才能省略参数名.
- 如果返回类型和函数名在一行放不下, 分行.
- 如果返回类型与函数声明或定义分行了, 不要缩进.
- 左圆括号总是和函数名在同一行.
- 函数名和左圆括号间永远没有空格.
- 圆括号与参数间没有空格.
- 左大括号总在最后一个参数同一行的末尾处, 不另起新行.
- 右大括号总是单独位于函数最后一行, 或者与左大括号同一行.
- 右圆括号和左大括号间总是有一个空格.
- 所有形参应尽可能对齐.
- 缺省缩进为 2 个空格.
- 换行后的参数保持 4 个空格的缩进.
未被使用的参数如果其用途不明显的话, 在函数定义处将参数名注释起来:
void Circle::Rotate(double /*radians*/) {}
3.if-else
1.if 和左圆括号间都有个空格
2.简短的条件语句允许写在同一行. 只有当语句简单并且没有使用 else 子句时使用.
3.如果语句中某个 if-else 分支使用了大括号的话, 其它分支也必须使用
4.循环和开关选择语句
switch (var){ case 0: // 2 空格缩进 { ... // 4 空格缩进 break; } case 1: { ... break; } default: { assert(false); }}
//空循环体应使用 {} 或 continue, 而不是一个简单的分号.while (condition) { // 反复循环直到条件失效.}for (int i = 0; i < kSomeNumber; ++i) {} // 可 - 空循环体.while (condition) continue; // 可 - contunue 表明没有逻辑.
5.布尔表达式
//逻辑与 (&&) 操作符总位于行尾:if (this_one_thing > this_other_thing && a_third_thing == a_fourth_thing && yet_another && last_one) { ...}
6.类格式
- 所有基类名应在 80 列限制下尽量与子类名放在同一行.
- 关键词 public:, protected:, private: 要缩进 1 个空格.
- 除第一个关键词 (一般是 public) 外, 其他关键词前要空一行. 如果类比较小的话也可以不空.
- 这些关键词后不要保留空行.
- public 放在最前面, 然后是 protected, 最后是 private.
7.构造函数初始值列表
构造函数初始化列表放在同一行或按四格缩进并排多行.
// 如果所有变量能放在同一行:MyClass::MyClass(int var) : some_var_(var) { DoSomething();}// 如果不能放在同一行,// 必须置于冒号后, 并缩进 4 个空格MyClass::MyClass(int var) : some_var_(var), some_other_var_(var + 1) { DoSomething();}// 如果初始化列表需要置于多行, 将每一个成员放在单独的一行// 并逐行对齐MyClass::MyClass(int var) : some_var_(var), // 4 space indent some_other_var_(var + 1) { // lined up DoSomething();}// 右大括号 } 可以和左大括号 { 放在同一行// 如果这样做合适的话MyClass::MyClass(int var) : some_var_(var) {}
命令空间格式化
命名空间内容不缩进.
namespace {void foo() { // 正确. 命名空间内没有额外的缩进. ...}} // namespace//声明嵌套命名空间时, 每个命名空间都独立成行.namespace foo {namespace bar {
例外情况
- 不要使用匈牙利命名法 (比如把整型变量命名成 iNum). 使用 Google 命名约定, 包括对源文件使用 .cc 扩展名.
- Windows 定义了很多原生类型的同义词 (YuleFox 注: 这一点, 我也很反感), 如 DWORD, HANDLE 等等. 在调用 Windows API 时这是完全可以接受甚至鼓励的. 即使如此, 还是尽量使用原有的 C++ 类型, 例如使用 const TCHAR * 而不是 LPCTSTR.
- 使用 Microsoft Visual C++ 进行编译时, 将警告级别设置为 3 或更高, 并将所有警告(warnings)当作错误(errors)处理.
- 不要使用 #pragma once; 而应该使用 Google 的头文件保护规则. 头文件保护的路径应该相对于项目根目录 (Yang.Y 注: 如 #ifndef SRC_DIR_BAR_H_, 参考 #define 保护 一节).
- 除非万不得已, 不要使用任何非标准的扩展, 如 #pragma 和 __declspec. 使用 __declspec(dllimport) 和 __declspec(dllexport) 是允许的, 但必须通过宏来使用, 比如 DLLIMPORT 和 DLLEXPORT, 这样其他人在分享使用这些代码时可以很容易地禁用这些扩展.
源代码风格检测
下载
下载cpplint源码
下载python2.7(必须使用该版本,3.6版本无效)
检测C++源码风格
参考: cpplint 嵌入VS平台上使用心得
参考:Google代码规范工具Cpplint的使用
在线编程测试
多种语言在线测试
- Google C++ 编码风格-笔记
- Google Java编码风格
- Google C++ 编码风格精简
- Google C++ 编码风格精简
- Google C++ 编码风格精简
- Google C++ 编码风格精简
- google代码风格(C++)
- C编码风格
- Linux C编码风格
- linux C编码风格
- C语言编码风格
- C语言编码风格
- Google Objective-C 风格指南
- Linux内核编码风格--C程序代码风格
- Objective-C编码风格规范
- Linux C语言编码风格
- Google C++风格指南 阅读笔记
- GOOGLE C++编程风格笔记(一)
- 设计一个名为Circle2D的类
- STM32之内部3个ADC+2个DMA同时采集14路信号
- usaco6.1.2 A Rectangular Barn
- 65个面试常见问题技巧回答
- Java OOP
- Google C++ 编码风格-笔记
- web项目浏览器打开遇到问题:HTTP Status 503
- oracle什么时候应该commit(提交事务)
- 使用jstack命令分析线程状态
- 关于Activity的生命周期和onsaveinstancestate很好的两篇文章
- Windows下升级NodeJS和npm的版本
- 读取手机第一条短信内容和监听手机新收到的短信
- Android Studio添加本地或远程依赖
- Codeforces 536B Tavas and Malekas kmp找所有与前缀匹配的后缀