C++ 编程规范 101
2019年夏
组织和策略问题
第0条:不要拘泥与小节
-
了解那些东西不应该标准化
-
保持单个项目(单个文件)的风格一致性
-
提倡更简单更短小的函数.
第1条:在高警告级别进行编译
-
成功的构建应该是无声无息的(没有警告的)
-
例外:烦人或虚假的警告
第2条:使用自动构建系统
第3条:使用版本控制系统
-
几乎所有大一点的项目都需要一个以上开发人员和一周以上的开发时间[控制变更十分重要]
-
即使单独工作的开发人员也有脑子短路的瞬间
-
例外:只有一个程序员且只需一周的项目
-
使用版本控制系统
第4条:在代码审查上投入
设计风格
第5条:一个实体应该只有一个紧凑的指责
-
一个实体,一次只解决一个问题
-
好的商业理念能够一句话概括
-
在C++标准语言中,std::basic_string是一个臭名昭著的不良设计
第6条 正确,简单和 清晰 第一 [重要]
-
正确优于速度,简单优于复杂,清晰优于机巧
-
程序必须为阅读它的人而写
-
避免使用程序设计语言中的冷辟特性,应该使用最简单有效的技术
第7条:编程中知道何时及如何考虑伸缩性
-
永远不要使用指数复杂性的算法
-
尽可能使用线性算法
-
小心数据爆炸性增长
第8条:不要进行不成熟的优化
第9条:不要进行不成熟的劣化
第10条:尽量减少全局和共享数据
第11条:隐藏信息
-
不要公开提供抽象实体的内部信息
-
例外:白盒测试,struct
第12条懂得何时及如何进行并发性编程
第13条确保资源为对象所拥有,使用显式的RAII和智能指针
-
RAII:资源获取即初始化
-
RAII:小心复制构造和赋值
-
绝对不要在一个语句中分配一个以上资源
编程风格
第14条:宁要编译时,链接时错误;也不要运行时错误[重要]
-
编译时检查与数据无关,可信度更高
-
C++最强大的静态检查工具之一:自身的静态类型检查
-
运行时错误:断言;异常处理
第15条:积极使用const
第16条:避免使用宏
第17条: 避免使用魔数
第18条:尽可能局部的声明变量
第19条:总是初始化变量
-
一切从白纸开始:未初始化的变量是C++程序中的错误的常见来源
第20条:避免函数过长,避免嵌套过深
-
短胜于长,平胜于深;
-
高扬老师:一个函数不超过15行[着实受用]
-
为什么人阅读代码很困难?难以准确维护上下文;记忆力差
-
每一级嵌套都会增加阅读代码时的脑力消耗
第21条:避免跨编译单元的初始化依赖
第22条:尽量减少定义性依赖.避免循环依赖
第23条:头文件应该自给自足
第24条:总是编写内部#include保护符,绝不要编写外部#include保护符
#ifndef FILE_NAME_H
#def FILE_NAME_H
...
#endif
函数与操作符
第25条:正确的选择通过值,(智能)指针或者引用传递参数
第26条:保持重载操作符的自然语义[重要]
第27条:有限使用算数操作符和赋值操作符的标准形式
第28条: 优先使用++和–的标准形式,优先调用前缀形式
第29条: 考虑重载以避免隐含类型转换
第30条: 避免重载&&,||和,(逗号)
-
明智就是知道何时应该适可而止
-
&&与|| 会短路求值,逗号则无法保证从左到右求值
-
表达式模板是一个例外,设计它的目的就是用来捕获所有操作符
第31条: 不要编写依赖于函数参数求值顺序的代码
类的设计与继承
-
friend紧密度第一,继承紧密度第二
-
极简主义->抽象
第32条: 弄清所要编写的是 哪种 类
-
值类: 模仿C++ 的内置类型;基类,模板类,异常累{哈哈},附属
第33条: 用小类代替巨类[重要]
-
分而治之:小类更容易编写更易于保证争确,测试和使用.
-
巨类会削弱封装性
第34条: 用组合代替继承
-
继承会增加耦合性
-
组合的优点:更好的编译时隔离,更短的编译时间
-
除非需要继承的功能,否则不要忍受其弊端
第35条: 避免从 并非要设计成基类的类中继承
第36条: 优先提供抽象接口
-
抽象借口完全由(纯)虚函数构成的抽象类;没有数据成员也没有成员函数的实现
-
抽象借口能够让我们集中精力保证抽象的正确性
-
抽象类定义功能,而不实现功能.
-
总之,策略应该上推,实现应该下放
-
{除了接口都可以修改}
第37条: 公用继承即可替换性.继承不是为了重用,而是为了被重用
-
公用继承,能够使基类的指针或者引用实际指向某个派生类的对象
-
基类子类关系:is-a;work-like-a
-
新的需求应该由新的代码来满足
-
模板:静态多态
-
例外:策略类和混入类(mixins)通过公用继承添加行为
第38条: 实施安全的改写
第39条: 考虑将虚函数声明为非公用的,将公用函数声明为非虚的
-
将数据成员设计为private
-
在基类中进行修改代价高昂
第40条: 要避免提供隐式转换
-
并非所有的变化都是进步,隐式转换带类的影响经常是弊大于利
-
应用显式转换代替隐式转换 ,explict
第41条: 将数据成员设为私有的,无行为的聚集(C的struct)除外
-
信息隐藏是优秀软件工程的关键
-
主要由get/set函数组成的类可能是一种设计不良的表现
第42条: 不要公开内部数据
-
数据隐藏是一种强大的抽象方式,也是强大的模块化机制.
-
const是浅的,不会通过指针传播.
第43条: 明智的使用Pimpl
-
Pimpl:将所有私有成员聚合在一个不透明的指针的后面
-
C++将私有成员指定为不可访问的,但并没有指定为不可见的.
-
Pimpl惯用法,使其真正不可见
第44条: 优先编写非成员非友元函数
第45条: 总是一起提供new 和delete
第46条: 如果提供专门的new,应该提供所有标准形式(普通,就地和不抛出)
-
operator new();的三种重载形式都要
void * operator new(std::size_t);//普通
void * operator new(std::size_t,viod*);//就地
void * operator new(std::size_t,std::nothrow_t)throw(); //不抛出
构造\析构与复制
-
四大特殊成员函数:默认构造函数,复制构造,复制赋值,析构函数
第47条: 用同样的顺序 定义和初始化成员变量
第48条: 在构造函数中用初始化代替赋值
第49条: 避免在构造函数和析构函数中调用虚函数
第50条: 将基类析构函数设为公用且虚拟的,或者保护且非虚拟的
-
基类指针执行删除操作:基类析构函数设为公用且虚拟的
-
基类指针不执行删除操作:非公有且非虚拟的
-
编写一个基类就是定义一个抽象.
-
第51条: 析构函数\释放和交换绝对不能失败[重要]
第52条: 一致的进行复制和销毁
第53条: 显示的启用或者禁止复制
-
方法: T(const T&); T& operator=(const T&);
-
即只声明,不定义
第54条: 避免切片,在基类中考虑用克隆代替复制
第55条: 使用赋值的标准形式
第56条: 只要可行,就提供不会失败的swap(而且要正确的提供)
命名空间与模块
第57条: 将类型及其非成员函数接口置于同一命名空间中
第58条: 应该将类型和函数分别置于不同的名字空间中,除非有意想让他们一起工作[重要]
-
避免将类型和模板化函数,操作符放在相同的名字空间中
第59条: 不要在头文件中或者#include之前编写命名空间using
第60条: 要避免在不同模块中分配和和释放内存
第61条: 不要在头文件中定义具有链接的实体
第62条: 不要允许异常跨越模块边界传播
第63条: 在模块的接口中使用具有良好可移植性的类型
-
生在(模块的)边缘,必须格外小心.
-
接口类型,都能准确理解[C++没有二进制接口]
模板与泛型
第64条: 理智的结合静态多态性和动态多态性[重要]
-
动态多态性:有虚函数的类;通过指针简介操作实例
-
静态多态性:模板类,模板函数
第65条: 有意的进行显式自定义
第66条: 不要特化函数模板
第67条: 不要无意地编写不通用的代码
错误处理与异常
-
问题不在于我们是否会范编程错误,而在于我们是否安排编译器和工具查找错误.
第68条: 广泛的使用断言记录内部假设和不变式
-
信息论原理:一个事件所含的信息量与该事件发生的概率是成反比的.
-
禁用assert: 设置NDEBUG 宏
第69条: 建立合理的错误处理策略,并严格遵守
第70条: 区别错误与非错误
第71条: 设计和编写错误安全代码
第72条: 优先使用异常报告错误
第73条: 通过值抛出,通过引用捕获
第74条: 正确的报告,处理和转换错误
第75条: 避免使用异常规范
stl容器
第76条: 默认使用vector,否则选择其他合适的容器
第77条: 用vector和string代替数组
第78条: 使用vector和string::c_str与非C++API交换数据
第79条: 在容器中只存储 值和智能指针[重要]
第80条: 用push_back代替其他扩展序列的方式
第81条: 多用范围操作,少用单元素操作
第82条: 使用公认的惯用法真正的压缩容量,真正的删除元素
stl算法
第83条: 使用带检查的STL实现
第84条: 用算法调用代替手工编写的循环
第85条: 使用正确的STL查找算法
第86条: 使用正确的STL排序算法
第87条: 使谓词成为纯函数
-
谓词就是返回是或否的函数对象
-
纯函数:函数的结果由参数决定
第88条: 算法和比较器的参数应多用函数对象少用函数
第89条: 正确编写函数对象
类型安全
第90条: 避免使用类型分支,多使用多态
第91条: 依赖类型而非其表示方式{重要}
第92条: 避免使用reinterpret_cast
第93条: 避免对指针使用static_cast
第94条: 避免强制转换const
第95条: 不要使用C风格的强制转换
第96条: 不要对非POD进行mencpy或者memcmp
-
POD:{plain old data} C的struct和union等.该概念没有明确定义.
第97条:不要使用联合{union}重新解释表示方式
第98条: 不要使用可变长参数{…}
第99条: 不要使用失效对象,不要使用不安全函数
第100条: 不要多态的处理数组
一个人的常量可能是另一个人的变量
.
任何规则都有例外
包括以上所有的规则,量子通讯的绝对安全也破灭了
代码编写扁平化
为什么叫C++而不叫++C:因为先有的C,后有C++
KISS原则
完