命名空间与模块
将类型及其成员函数接口置于同一命令空间中
- 如果要将非成员函数(特别是操作符和辅助函数)设计成类X的接口的一部分,那么就必须在与X相同的命名空间中定义它们,以便正确调用。
应该将类型和函数分别置于不同的命名空间,除非有意让它们一起工作
不要在头文件或者include之前使用using命名空间
要避免在不同的模块中分配和释放内存
不要在头文件中编写具有链接的实体
- 具有链接的实体,包括命名空间的变量或函数,都需要分配内存。
- 在头文件中定义这样的实体将导致连接时错误或内存的浪费。
不要允许异常跨越模块边界传播
- 不要在两端代码之间传播异常,除非能够控制用来构建两段代码的编译器和编译选项,否则模块可能无法支持可兼容地实现异常传播。
- 应用程序必须在以下地方有捕获所有异常的catch兜底语句:
- 在main函数的附近。
- 在从无法控制的代码中执行回调附近。
- 在线程边界附近。
- 在模块接口边界的附近。
- 在析构函数内部。
在模块的接口中使用具有良好移植性的类型
- 不要让类型出现在外部模块的接口中,除非能够确保所有的客户代码都能正确地理解该类型。
模板与泛型
理解的结合静态多态和动态多态
- 静态多态和模板类和模板函数有关。动态多态是以某些类的形式出现的,这些类里面有虚函数和间接操作的实例(通过指针或引用)。
- 动态性擅长:
- 基于超集/子集关系的统一操作。具有超集/子集关系的不同类可以被统一处理。
- 静态类型检查。
- 动态绑定和分别编译。
- 二进制接口。
- 静态多态擅长:
- 基于语法和语义接口的统一操作。
- 静态类型检查。
- 效率。
- 用静态多态性辅助动态多态性。
- 用动态多态性辅助静态多态性。
- 任何其他的结合。
有意地进行显式自定义
- 在编写模板时,应该有意地、正确地提供自定义点,并清晰地记入文档。
- 在使用模板时,应该了解模板想要你如何进行自定义以将其用于你的类型,并且正确地自定义。
不要特化模板函数
- 只有在能够正确实施的时候,特化才能起到好作用:在扩展其他人的函数模板时,要避免尝试编写特化代码;
- 相反,要编写函数模板的重载,将其放在重载所用的类型的命名空间中。
- 特化函数模板很不直观:
- 不可能部分地特化函数模板,只能完全特化。
- 函数模板特化决不能参与重载。
不要无意地编写不通用的代码
- 使用!=代替<对迭代器进行比较。
- 使用迭代代替索引访问。
- 使用
empty()
代替size()==0
。 - 使用层次结构中最高层的类提供需要的功能。
- 编写常量正确的代码。
错误处理与异常
广泛的使用断言记录内部假设和不变式
建立合理的错误处理策略,并严格遵守
- 鉴别
- 严重程度
- 检查
- 传递
- 处理
- 报告
区别错误与非错误
- 函数是一个工作单元,失败应该被视为错误,或根据其对函数的影响而定。
设计和编写错误安全代码
优先使用异常报告错误
- 异常不能不加修改地忽略。
- 异常是自动传播的。
- 有了异常处理,就不必在控制流的主线中加入错误处理和恢复了。
- 对于从构造函数和操作符报告错误来说,异常处理要优于其他方案。
通过值抛出,通过引用捕获
正确地报告、处理和转换错误
避免使用异常规范
- 不要在函数中编写异常规范,除非不得已而为之。
STL容器
默认时使用vector
- 编程时正确、简单和清晰是第一位的。
- 编程时只有必要时才考虑效率。
- 尽可能编写事务性的、强错误安全的代码,而且不使用失效对象。
- vector性质:
- 保证具有所有容器中最低的空间开销。
- 保证具有所有容器中对所存放元素进行存取的速度最快。
- 保证具有与生俱来的引用局部性,也就是说容器中相邻对象保证在内存中也相邻,这是其他标准容器无法保证的。
- 保证具有与C语言兼容的内存布局,这与其他标准容器也不同。
- 保证具有所有容器中最灵活的迭代器(随机访问迭代器)。
- 几乎肯定具有最快的迭代器(指针、或性能相当的类、在非调试模式下迭代器类经常可以被编译成与指针具有相同的速度),比其他所有容器的迭代器都快。
用vector和string代替数组
- 能够自动管理内存
- 具有丰富的接口
- 与C的内存模型兼容
- 能够提供更大范围的检查
- 支持上述特性并未牺牲太多效率
- 有助于优化
使用vector和string与非C++API交换数据
- vector和string的string::c_str是与非C++API通信的道路。
在容器中只存储值和智能指针
用push_back代替其他扩展序列的方式
多用范围操作,少用单元素操作
使用公认的惯用法真正地压缩容量,真正地删除元素
STL算法
使用带检查的STL实现
用算法代替手工编写的循环
使用正确的STL查找算法
使用正确的STL排序算法
使谓词成为纯函数
- 谓词就是保持是与否的函数对象,返回值通常是bool
算法和比较器的参数应多用函数对象少用函数
正确编写函数对象
- 可适配性
- 具有pimpl
- 具有函数调用操作符
类型安全
避免使用类型分支,多使用多态
依赖类型,而非其表达方式
- C++标准对类型的内存关系只有如下几个规定:
- 整数保证以2为基数
- 负整数保证以2的补码形式表示
- 普通旧式数据(POD)类型的内存布局与C兼容:成员变量根据声明的顺序存储。
- int至少有16位
- int不会恰好为32位,或者其他某个特定的固定大小
- 指针和int的带下并不总是相同的,而且并不总是能任意互相转换的
- 类(甚至是POD)的成员之间为了对齐可能存在间隙
- offsetof只适用于POD,而非所有类
- 类可能会具有隐藏字段
- 指针可能看起来与整数完全不同
- 不能对自动变量的内存布局或者栈增长的方向做任何可移植性方面的假设
- 函数指针的大小可能与void*不同,虽然有些API要求我们认为两者大小相同
- 由于对齐问题,无法总是在任意内存地址写入任意对象,即使有足够的空间
避免使用reinterpret_cast
- 不要尝试使用reinterpret_cast强制编译器将某个类型对象的内存表示重新解释成另一种类型的对象。
- 这违反了维护类型安全性的原则,尤其可怕的是,reinterpret_cast甚至不能保证是否能够达到这一目的,也无法保证其他功能。
避免对指针使用static_cast
- 不要对动态对象的指针使用static_cast,安全的替代方案有:
- dynamic_cast
- 重构
- 重新设计
避免强制转换const
- 强制转换const有时会导致未定义的行为。
不要使用C风格的强制转换
- C语言风格的强制转换根据上下文具有不同(而且经常很危险)的语义,而所有这些都隐藏在相同的语法背后。
不要对非POD进行memcpy操作或者memcmp操作
- 不要用memcpy或者memcmp来复制或比较任何对象,除非有什么对象的布局就是原始内存。
不要使用联合重新解释表达方式
- 通过在union中写入一个成员而读取另一个的滥用方式可以获得“无需强制转换的强制转换”。比起reinterpret_cast更阴险,也更难预测。
不要使用可变长参数...
- ...是来自C语言的危险产物,要避免使用这种可变长参数,应该改用高级的C++结构和库。
不要使用失效对象,不要使用不安全函数
- 失效对象:
- 已销毁对象
- 语义失效对象
- 从来都有效的对象
不要多态地处理数组
- 多态地处理数组是绝对的类型错误,而且编译器有可能不会做出任何提示。
本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ Spdlog记述:四09/16
- ♥ C++17_第二篇12/22
- ♥ STL_内存处理工具05/02
- ♥ 打包_7z生成自解压打包exe07/11
- ♥ C++标准模板库编程实战_智能指针11/30
- ♥ Spdlog记述:二07/09