Google C++代码风格指南翻译简化版(google c++ code style guide)【三】

Google 编程规范本地化、简化。如有需要,可参考 Google C++ code style guide原文:
http://google.github.io/styleguide/cppguide.html

本文档基于网上流传的 Google C++编程风格指南,由 edisonpeng(2009/3/25)整理

本地化简化由MISAS开发团队使用。在此分享以供各开发团队参考。

目录

@[toc]

C++ 类

类是C++中基本的代码单元,被广泛使用。

构造函数

构造函数只初始化没有实际意义的成员变量。需要初始化的数据在Init()方法中集中初始化。

定义:在构造函数中执行初始化操作

优点:排版方便,无需担心类是否初始化

缺点: 在构造函数中执行初始化操作容易引起以下问题:

(1)构造函数中不易报告错误,不能使用异常

(2)操作失败会造成对象初始化失败,引起不确定状态

(3)构造函数内调用虚函数,调用不会派发到子类实现中,技术当前不需要子类实现,但依然是隐患

(4)如果有人创建该类的全局变量(这样做违背禁止类作为全局变量的准则),构造函数在main()函数执行前调用,有可能影响代码中使用flag的逻辑。

结论:
如果对象需要有意义的初始化,使用Init()方法合并需要初始化的内容,并增加一个成员变量标记该对象是否初始化成功。

默认构造函数

如果一个类定义了若干个成员变量又没有其他构造函数,需要定义一个默认构造函数,否则编译器将自动生成默认构造函数。

定义:新建一个没有参数的对象时,默认构造函数被调用,当使用new时,默认构造函数会被调用。

优点:默认将结构体初始化为“不可能的”值,使调试更加容易

缺点:对代码编写者来说,这是多余的工作。

结论:如果类中定义了成员变量,没有提供其他构造函数,则需要自定义一个默认构造函数(没有参数)。默认构造函数用于初始化对象,使对象内部状态一致、有效。

提供默认构造函数的原因:编译器自动生成的构造函数不会对对象进行初始化。

如果定义的类继承自现有类,且没有新增新的成员变量,则不需要为新类定义默认构造函数。

明确的构造函数

单参构造函数使用C++关键字explicit

定义:通常,只有一个参数的构造函数会发生隐式转换。避免此问题,可声明为explicit

优点:避免不合时宜的变换。

缺点:

结论:

所有单参析构函数必须是明确的,在类定义中加 explicit

拷贝构造函数可以不用声明为explicit

拷贝构造函数

仅在代码中需要拷贝一个类对象的时候使用拷贝构造函数;不需要拷贝时使用

DISALLOW_COPY_AND_ASSIGN。

定义:通过拷贝新建对象时可使用拷贝构造函数(特别是对象的传值时)。

优点:拷贝构造函数是的拷贝对象更加容易,STL容器要求所有内容可拷贝、可赋值。

缺点:C++中对象的隐式拷贝是导致很多性能问题和bugs的根源。拷贝构造函数降低了代码的可读性,相比按引用传递,跟踪按值传递的对象更加困难,对象修改的地方变的难以捉摸。

结论:禁止使用拷贝构造函数

结构体和类

仅当只有数据时使用struct,其他一概使用class。

注意:类和结构体的成员变量使用不同的命名规则

继承

尽量使用组合而不是继承。如果使用继承,只用public继承。

允许接口继承,尽量不使用实现继承。

多重继承

多重继承的使用场景:仅允许最多一个基类中包含实现,其他基类都是以Interface为后缀的纯接口。

接口(Interface)

接口是指满足特性条件的类,这些类以Interface为后缀(命名约定)。

定义:当一个类满足以下要求时,称之为纯接口:

(1)只有纯虚函数和静态函数(析构函数除外)

(2)没有非静态数据成员

(3)没有定义任何构造函数,或定义不含参数的protected构造函数

(4)如果是子类,也只能继承满足上述条件并以Interface为后缀的类

接口类不能被直接实例化,因为他声明了纯虚函数。为确保接口类所有实现可被正确销毁,必须为之申明虚析构函数。

优点:代码维护人员看到Interface就知道不能为该接口类实现函数或非静态数据成员

缺点:增加类名长度带来阅读不便。同时,接口细节不应暴露给用户。

结论:满足上述需求的类才使用Interface结尾,但满足也可以不使用。

操作符重载

除少数特定环境外,不要重载操作符。

定义:一个类合一定义诸如+、/等操作符,使其可以像内建类型一样直接使用。

优点:提高代码可读性,比如Equals()用==代替

缺点:

(1)混淆直觉,让开发者误以为一些耗时的操作像内建操作那样轻巧;

(2)查找重载操作符的调用处更加困难,查找Equals()比==调用容易的多。

(3)有的操作符可以操作指针,容易导致bugs

(4)副作用,如重载操作符&不能被前置声明

结论:

禁止重载操作符,尤其是赋值操作符严格禁止。

存取控制

将数据成员私有化,并提供相关的存取函数

声明次序

类中声明次序:public、protected、private,如果某一块没有,直接忽略。

每一块中,声明次序如下:

(1)typedefs 和 enums;

(2)常量;

(3)构造函数;

(4)析构函数;

(5)成员函数,含静态成员函数;

(6)数据成员,含静态数据成员;

宏 DISALLOW_COPY_AND_ASSIGN置于private之后,作为类的最后部分。

.cc文件中的函数定义应尽可能声明次序一致。

禁止将大型函数内联到类的定义中。

编写短小函数

函数长度尽量不超过40行,倾向选择短小、凝练的函数。

函数尽量短小、简单,便于他人阅读和修改代码。处理代码时如发现长函数,考虑分割为若干短小、易于管理的若干函数。

总结:

(1) 不在构造函数中做太多逻辑相关的初始化;

(2) 编译器默认构造函数不会初始化变量,如果定义了其他构造函数,编译器不再提供,需要编码这自行提供默认构造函数;

(3) 为避免隐式转换,需将单参数构造函数声明为 explicit;

(4) 为避免拷贝构造函数、赋值操作的滥用和编译器自动生成,可声明为private且无需实现;

(5) 仅在作为数据集合时使用struct;

(6) 组合>实现继承>接口继承>私有继承,子类重载的虚函数也要声明virtual关键字;

(7) 避免使用多重继承,唯一的使用场景是最多一个基类含实现,其他基类纯接口;

(8) 接口类类名以Interface为后缀,全部使用纯虚函数;

(9) 禁止重载操作符

(10) 存取函数一般内联在头文件中;

(11) 声明次序:public->protected->private;

(12) 函数体尽量短小、紧凑,功能单一。

版权声明

弈心博客


本文首发site_name,转载请附上博文链接!