Google开源项目风格指南-笔记
Google 开源项目风格指南——中文版Google C++ Style Guide1. 头文件前置声明:类似于函数的声明和定义,C++里类的声明和定义也是可以分开的。我们可以先声明而暂时不定义它,这种声明就称为类的前置声明。前置声明写法有一定的限制,只能定义指针或引用。#include的路径及顺序:您所依赖的符号 (symbols) 被哪些头文件所定义,您就应该包含(include)哪些头文件,
1. 头文件
类似于函数的声明和定义,C++里类的声明和定义也是可以分开的。我们可以先声明而暂时不定义它,这种声明就称为类的前置声明。
注意:前置声明的类是不完全类型(incomplete type),我们只能定义指向该类型的指针或引用,或者声明(但不能定义)以不完全类型作为参数或者返回类型的函数。
1.2 #include的路径及顺序
您所依赖的符号 (symbols) 被哪些头文件所定义,您就应该包含(include)哪些头文件,前置声明 (forward declarations) 情况除外。比如您要用到 bar.h
中的某个符号, 哪怕您所包含的 foo.h
已经包含了 bar.h
, 也照样得包含 bar.h
, 除非 foo.h
有明确说明它会自动向您提供 bar.h
中的 symbol。不过,凡是 cc 文件所对应的「相关头文件」已经包含的,就不用再重复包含进其 cc 文件里面了,就像 foo.cc
只包含 foo.h
就够了,不用再管后者所包含的其它内容。
在 #include
中插入空行以分割相关头文件, C 库, C++ 库, 其他库的 .h
和本项目内的 .h
是个好习惯。
3. 类
3.1 构造函数
构造函数不得调用虚函数, 或尝试报告一个非致命错误. 如果对象需要进行有意义的 (non-trivial) 初始化, 考虑使用明确的 Init() 方法或使用工厂模式。
3.2 隐式类型转换
在类型定义中, 类型转换运算符和单参数构造函数都应当用 explicit
关键字进行标记.
explicit:修饰只有一个参数的构造函数,以防止从参数类型到目标类类型的隐式转换。
隐式类型转换,是编译器自发的行为,所以安全是第一位。所以呢,我们可以得出一条很重要的结论:
- 隐式类型转换是从小到大的转换。在数据类型上表现是少字节数据类型,转换成多字节数据类型,保证数据的完整性;在类上表现,从子类转换成父类,保证类对象功能的正常。
- 隐式类型转换往往是安全的,但是它可能产生意想不到的危险。
class Test
{
public:
Test(int i);
};
Test t1 = 1;//正确,由于强制类型转换,1先被Test构造函数构造成Test对象,然后才被赋值给t1
Test t2(1);//正确
explic关键词的用法:
class Test
{
public:
explicit Test(int i);
};
Test t2 = 1;//编译报错
Test t2(2);//编译没问题
3.3 拷贝构造函数 & 移动构造函数
如果你的类型需要, 就让它们支持拷贝 / 移动. 否则, 就把隐式产生的拷贝和移动函数禁用。
如果你的类不需要拷贝 / 移动操作, 请显式地通过在 public
域中使用 = delete
或其他手段禁用之.
// MyClass is neither copyable nor movable.
MyClass(const MyClass&) = delete;
MyClass& operator=(const MyClass&) = delete;
移动构造函数:
有时候我们会遇到这样一种情况,我们用对象a初始化对象b,后对象a我们就不在使用了,但是对象a的空间还在呀(在析构之前),既然拷贝构造函数,实际上就是把a对象的内容复制一份到b中,那么为什么我们不能直接使用a的空间呢?这样就避免了新的空间的分配,大大降低了构造的成本。这就是移动构造函数设计的初衷。
对于指针,
- 拷贝构造函数中:采用深层复制;
- 移动构造函数中:采用浅层复制。
知识点:当类中同时包含拷贝构造函数和移动构造函数时,如果使用临时对象初始化当前类的对象,编译器会优先调用移动构造函数来完成此操作。只有当类中没有合适的移动构造函数时,编译器才会退而求其次,调用拷贝构造函数。
3.4. 结构体 VS. 类
仅当只有数据成员时使用 struct
, 其它一概使用 class
.
结构体:当定义结构体变量时,可以通过两种方式初始化它:使用初始化列表或构造函数。
3.5. 继承
- 使用组合通常比使用继承更适宜
- 如果使用继承,只使用公共继承
- 所有继承必须是public,想私有继承的话,应采取包含基类实例的方式替代
- 如果该类有虚函数,其析构函数也应该为虚函数
- 派生类重定义基类的虚函数时,该派生类函数也应声明为virtual函数
- 对于重载的虚函数或虚析构函数, 使用
override
, 或 (较不常用的)final
关键字显式地进行标记
C++ 11添加了两个继承控制关键字:override和final。override确保在派生类中声明的重载函数跟基类的虚函数有相同的签名。final阻止类的进一步派生和虚函数的进一步重载。
override:
表示此虚函数必定“重写”了基类中的对应虚函数。
final:C++11的关键字final有两个用途,第一,它阻止了从类继承;第二,阻止一个虚函数的重载。
(1) 作用在虚函数:表示此虚函数已处在“最终”状态,后代类必定不能重写这个虚函数。
(2) 作用在类:表示此类必定不能被继承
override明确地表示一个函数是对基类中一个虚函数的重载。更重要的是,它会检查基类虚函数和派生类中重载函数的签名不匹配问题。如果签名不匹配,编译器会发出错误信息。
3.6. 多重继承
只有当所有父类除第一个外都是 纯接口类 时, 才允许使用多重继承. 为确保它们是纯接口, 这些类必须以 Interface
为后缀.
3.9. 存取控制
将 所有 数据成员声明为 private
, 除非是 static const
类型成员
3.10. 声明顺序
- 将相似的声明放在一起, 将
public
部分放在最前 - 声明次序:
public
->protected
->private
; - 在各个部分中, 建议将类似的声明放在一起, 并且建议以如下的顺序: 类型 (包括
typedef
,using
和嵌套的结构体与类), 常量, 工厂函数, 构造函数, 赋值运算符, 析构函数, 其它函数, 数据成员.
更多推荐
所有评论(0)