C++核心准则

面向对象思考

共 3075字,需浏览 7分钟

 ·

2020-02-13 23:23

f5c0fe3845e63bc155cdc3256a7f3cb6.webp

介绍

序言

摘要

介绍

不必惊慌!

目标之外

实施建议

文档结构

P:基本原则

P.1:直接用代码表达想法

P.2 使用ISO标准C++写代码

P.3 表达意图

P.4 理想情况下,程序应该静态类型安全

X.3 使用variant代替union

X.4:使用span解决数组退化和越界访问

X.5:尽量不使用类型转换

P.5 编译时检查比执行时检查更好

P.6 不能在编译时检查的,应当可以在执行时检查

P.7 尽早捕捉执行时错误

P.8 不要泄漏任何资源

P.9 不要浪费时间和空间

P.10 不变的优于可变的

P.11 封装混乱的代码,而不是传播它们

P.12: 酌情使用支持工具

P.13: 酌情使用支持库

接口

I.1: 使接口清晰明确

I.2:避免非常数全局变量

I.6 表达前提条件最好使用Expects()

I.7 说明后置条件

I.8 表示后置条件最好使用Ensures()

I.10 如果必要任务失败了,使用异常发信号

I.11 永远不要使用原始指针或引用传递所有权

I.12 用not_null定义不能为空的指针

I.13 不要直接使用指针传递数组

I.22 避免对全局对象进行复杂的初始化

I.23 尽量不增加函数的参数个数

I.24 避免相同类型的无关参数相邻

I.25 在类体系中将抽象类定义成接口会更好

I.26 如果想要不同编译器都能适用的ABI,使用C风格规则子集

I.27 考虑使用指向实现的指针技术获得稳定的ABI

I.30 封装必要的违反

函数

F.1:将有意义的操作封装为精心命名的函数

F.2 一个函数只执行单一逻辑操作

F.3 保持函数简短

F.6 如果函数不会抛出异常,则声明为noexcept

F.7 对于通常的使用场景,应该使用T*或T&参数而非智能指针。

F.8 优先选择纯函数

F.9:不用的参数应该去掉名字

F.15 优先使用简单、常规的方式传递参数

F.16 对于输入参数来说,拷贝代价小的传值,其他传递const参照

F.17 输入/输出参数传递非常量引用

F.18 使用X&&传递“将会发生数据移动”的参数并实施数据移动

F.19 对于只传递不处理的参数,使用模板类型TP&&并在传递时使用std::forward

F.20 输出结果时更应该使用返回值而不是输出参数

F.21 如果需要返回多个输出值,最好返回结构体或者tuple

F.22 使用T*或onwer指明唯一对象

F.23 使用not_null表明“空”是无效值

F.24 使用span或者span_p表示半开序列

F.25 使用zstring或not_null表示C风格字符串

F.26 使用unique_ptr代替指针传递所有权

F.27 使用shared_ptr共享所有权

F.42 T*返回值应该只用来指明位置

F.43 永远不要返回指向局部对象的指针或引用

F.44 在不希望得到拷贝而且不需要返回值为空时返回T&

F.45 不要返回右值引用

F.46 main函数的返回值类型是整数

译边学-F.4 赋值运算符应该返回T&

F.48 不要返回使用std:move从局部变量获得的右值引用

F.50:不愿意使用函数时使用lambda表达式

F.51:如果可以,优先选择缺省参数而不是重载

F.52:在lambda表达式中使用引用形式捕捉局部变量

+核心准则F.53:非局部使用变量时避免使用值捕捉

F.54:不要隐式捕捉this指针

F.55 不要使用可变参数

类和继承

C.1:组织相关数据形成结构体或者类

C.2:包含不变式时用class,否则用struct定义类

C3

C.4:只有直接访问表达的函数,才应该成为成员

C.5:将帮助函数和它们支持的类放在同一命名空间

C.6:不要在一条语句内声明类或枚举值的同时又定义该类型的变量

C.8:存在非公有成员时,使用class而不是struct定义类

C.9:最小限度暴露成员

具体类型

C.10:具体类型要好于类继承

C.11:让具体类型符合常规

构造/析构和复制

C.20:尽量避免定义默认操作

C.21:默认操作要定义就全定义,要禁止就全禁止

C.22:保持默认操作的一贯性

C.30:如果一个类需要明确的销毁动作,定义析构函数

C.31:类请求的所有资源必须在析构函数释放

C.33:如果类包含拥有所有权的指针成员,定义析构函数

C.35:基类的析构函数要么是公开的虚函数,要么是保护的非虚函数

C.36:析构函数不应该失败

C.37:保证析构函数不会抛出异常

C.40:如果类包含不变式,则定义构造函数

C.41:构造函数生成的对象应该被完全初始化

C.42:如果构造函数不能生成合法对象就抛出异常

C.43:保证(值类型)可拷贝类有默认构造函数

C.44:默认构造函数最好简单而且不会抛出异常

C.45:使用类内初始化器初始化数据成员

C.46:默认状态下明确定义单参数构造函数

C.47:按照成员变量声明的次序定义和初始化数据成员

C.48:如果构造函数需要用常数初始化成员,使用类内初始化器更合适

C.49:构造函数中应该做的是初始化而不是赋值

C.50:如果在构造过程中需要“虚行为”,使用工厂函数

C.51:使用委托构造函数实现所有构造函数的共通动作

C.52:合理使用继承的构造函数

C.60: 拷贝赋值运算符应该是以const&为参数,返回非常量引用类型的非虚函数

C.61:拷贝操作应该具有拷贝的效果

C.62:保证拷贝赋值对自我赋值安全

C.63:保证移动赋值运算符为非虚函数,参数类型为右值引用,返回值为常量引用类型

C.64:移动操作在完成移动之后,移动源对象应该保持有效状态

C.65:让移动操作对自赋值安全

C.66:保证移动操作不会抛出异常

C.67:多态类应该抑制拷贝

C.80:如果明确希望使用默认语义,使用=default

C.81:如果不需要默认(同时不需要其他选项)行为,使用=delete禁止它们C.82:不要在构造函数或析构函数中调用虚函数

C.83:对于值类类型,考虑提供一个不会抛出异常的交换函数

C.84:swap函数不应该失败

C.86:保证==语义遵守操作数规则并不会抛出异常

C.87:小心基类的相等运算符

C.89:保证哈希不会抛出异常

C.90:依靠构造函数和赋值运算符,而不是内存初始化和内存拷贝‍

容器和资源管理

C.100:定义容器时遵从STL标准‍

C.101:赋予容器值语义‍

C102-109:容器的基本要求

类继承(面向对象)

C.120:类层次体系只用于表现固有的阶层结构‍

C.121:如果基类被用来定义接口,保证它是一个纯虚类‍

C.122:需要完全隔离接口和实现时用抽象类作为接口‍

C.126:抽象类通常不需要构造函数‍

C.127:包含虚函数的类应该有虚析构函数或保护析构函数‍

C.128:虚函数应该明确定义为virtual,overide或者final

C.129:设计类层次关系时,区分实现继承和接口继承‍

C.130:实现多态类的深拷贝时,虚clone函数要比拷贝构造函数/赋值运算符好

C.131: 避免无意义的getters和setters‍

C.132:不要没有理由就将函数声明为虚函数‍

C.133:避免保护型数据成员‍

C.134:确保所有非常量数据成员具有相同的访问权限‍

C.135:使用多重继承表现多个不同种类的接口‍

C.136:使用多重继承表现“实现属性”的组合

C.137: 使用虚基类避免过于一般的基类‍

C.138:使用using为派生类生成重载函数集合‍




觉得本文有帮助?请分享给更多人。

关注【面向对象思考】轻松学习每一天!

面向对象开发,面向对象思考!

浏览 59
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报