开课吧:分享C++代码的整洁之道!
大家好,今天分享的主题是:C++代码整洁之道。
整洁的代码在团队中无疑是很受欢迎的,可以高效的被其它成员理解和维护,本文参考《C++代码整洁之道》和《Google C++编码规范》,结合自己的一些想法整理如下:
C++本身作为面向对象语言,首先介绍下面向对象一般涉及到的开发原则。
一、面向对象开发原则
1、依赖倒置原则:
针对接口编程,依赖于抽象而不依赖于具体,抽象(稳定)不应依赖于实现细节(变化),实现细节应该依赖于抽象,因为稳定态如果依赖于变化态则会变成不稳定态。
2、开放封闭原则:
对扩展开放,对修改关闭,业务需求是不断变化的,当程序需要扩展的时候,不要去修改原来的代码,而要灵活使用抽象和继承,增加程序的扩展性,使易于维护和升级,类、模块、函数等都是可以扩展的,但是不可修改。
3、单一职责原则:
一个类只做一件事,一个类应该仅有一个引起它变化的原因,并且变化的方向隐含着类的责任。
4、里氏替换原则:
子类必须能够替换父类,任何引用基类的地方必须能透明的使用其子类的对象,开放关闭原则的具体实现手段之一。
5、接口隔离原则:
接口最小化且完备,尽量少public来减少对外交互,只把外部需要的方法暴露出来。
6、最少知道原则:
一个实体应该尽可能少的与其他实体发生相互作用。
7、将变化的点进行封装
做好分界,保持一侧变化,一侧稳定,调用侧永远稳定,被调用侧内部可以变化。
8、优先使用组合而非继承
继承为白箱操作,而组合为黑箱,继承某种程度上破坏了封装性,而且父类与子类之间耦合度比较高。
9、针对接口编程
而非针对实现编程,强调接口标准化。
二、C++开发原则
通过上述面向对象开发原则的理解可以细化到具体C++开发原则。
1、保持简单和直接原则:
保持代码尽可能简单,如果需求需要的话,才在代码中引入灵活的可变点,只添加那些可使整体变得更简单的局部复杂的东西。
2、不需要原则:
总是在你真正需要的时候再实现他们,而不是在你只是预见到你将来会需要他们而去实现,在真正需要的时候再写代码,那时再重构也来得及。
3、避免复制原则:
不要复制,不要重复,这是相当危险的操作,你修改一处代码的时候总能记得去修改另外一处或另外多处你曾经复制的代码吗?
4、信息隐藏原则:
一段代码调用了另外一段代码,调用者不应该知道被调用者代码的实现,否则调用者就有可能修改被调用者的实现来实现某些功能,而这有可能引发其它调用者的bug。
5、高内聚低耦合原则:
类似单一职责原则,明确每个模块的具体责任,尽量少的依赖于其它模块。
6、最少惊讶原则:
函数功能要与函数名字功能一致,难道你要在一个getter()函数去更改成员变量的值吗?
7、更干净原则:
离开露营地的时候,应让露营地比你来之前还要干净,当发现代码中有需要改进或者风格不好的地方,应该立刻改掉,不要care这段代码的原作者是谁,也不要care这是谁的模块,代码所有权是集体的,每个团队成员在任何时候都应该可以对任何代码进行更改和扩展。
三、注重单元测试
重要性就不多说了,防患于未然,构建大型系统尤其需要进行单元测试,保证代码质量,可以防患于未然。
一般都讲究测试驱动开发,开发一个功能首先要想好怎么测试,先把测试代码写好,再去开发对应的需求。通过单元测试也有利于开发者更好的进行接口的设计,主要说下良好的单元测试的原则。
1、单元测试的原则
保证单元测试的代码的质量,单元测试的代码也是代码,不应该和产品代码区别对待,而且单元测试的代码再写出bug更影响测试效率。
2、单元测试的命名
每个测试单元需要根据具体测试内容进行相应的命名,方便定位分析问题,好的命名如果出现问题时通过测试单元的名字基本就可以定位问题。
3、保证单元测试的独立性
每个测试单元都是独立的,不依赖于其它测试单元,不要构建测试单元的上下文,上面的测试单元出问题影响到下面的单元测试的设计是很不友好的。
4、尽量保证一个测试单元使用一个断言
保证测试单元内部的一个相对独立性,上面的断言阻碍了下面的断言测试也是不好的设计。
5、保证单元测试环境的独立
保证每个测试单元都有独立的环境,不依赖于其它环境,每个测试单元都要是个独立的可运行的实例,每个单元测试结束后记得清理环境。
6、没必要都做单元测试
没必要对第三方库和外部系统做单元测试,只对自己写的代码进行测试。
7、单元测试尽量不要涉及数据库
数据库的状态是全局的,测试不能保证独立性,而且数据库的访问也是缓慢的,影响单元测试的速度,如果真的需要可以模拟数据库在内容中进行测试,其实通常是在系统集成和系统测试级别时去测试数据库。
8、测试代码与产品代码
不要混淆测试代码和产品代码,产品代码中不应依赖测试代码。
9、测试必须要快速执行
确保秒级别,大型系统的单元测试也就几分钟而已,单元测试不要访问数据库、磁盘、网络等外设。
10、找一些测试替身
例如有些数据需要通过网络获取,那可以利用依赖注入做一个网络替身的类模拟这些数据的产生,可以研究研究Google mock。
四、良好的命名
无论是什么语言,函数和变量的良好命名都是很有必要的,通过函数的名字我们就可以知道这个函数里代码的作用,而不是通过写注释。
1、文件命名
文件名字要全部小写,中间用_相连,后缀名为.cc和.h
2、类型命名
类型名称的每个单词首字母均大写, 不包含下划线: MyExcitingClass, MyExcitingEnum.
3、变量命名
不要将变量的类型在名字中体现,这样以后变量类型改变的话还需要去改动变量名,充分利用IDE的功能,变量 (包括函数参数) 和数据成员名一律小写, 单词之间用下划线连接. 类的成员变量以下划线结尾, 但结构体的就不用, 如: a_local_variable, a_struct_data_member, a_class_data_member_.
4、常量命名
声明为 constexpr 或 const 的变量, 或在程序运行期间其值始终保持不变的, 命名时以 “k” 开头, 大小写混合
5、函数命名
常规函数使用大小写混合, 取值和设值函数则要求与变量名匹配: MyExcitingFunction(), MyExcitingMethod(), my_exciting_member_variable(), set_my_exciting_member_variable().
6、枚举命名
和常量一致
7、Tip:
除非像swap函数里tmp那种一目了然,否则不要搞无意义的命名,函数名变量名字宁可特别长也要写清楚究竟是什么意思,不要用缩写,一个变量尽量在临近使用前才定义,可读性强也可更好利用cpu cache。
五、编辑器
团队可以统一使用相同的编辑器,个人目前使用的是VS Code编辑器,同时每个项目使用统一的.clang_format文件,统一规范代码格式,所有的换行符都要用LF格式,不要用CRLF格式,在右下角可以设置。
个人的.clang-format文件如下,是在google风格的基础上做了些修改:
六、编码规范要点总结
每个头文件都要使用#define避免被重复引用
或使用#pragma once,而#define方式更通用
1、鼓励在 .cc 文件内使用匿名命名空间或 static 声明. 使用具名的命名空间时, 其名称可基于项目名或相对路径. 禁止使用 using 指示, 禁止使用内联命名空间(inline namespace)
2、一行尽量不要超过120个字符,一个函数尽量不要超过40行,同时一个文件尽量控制在500行内.
3、所有的引用形参如不做改动一律加const,在任何可能的情况下都要使用 const或constexpr
4、new内存的地方尽量使用智能指针,c++11 就尽量用std::unique_ptr替代std::auto_ptr
5、合理使用移动语义,减少内存拷贝,参考左值引用、右值引用、移动语义、完美转发,你知道的不知道的都在这里
6、禁止使用 RTTI,尽量在编译期间就确定参数类型,不要搞运行时识别typeid这种代码
7、使用 C++ 的类型转换, 如 static_cast<>(). 不要使用 int y = (int)x 或 int y = int(x) 等转换方式
8、明确使用前置++还是后置++的具体含义,如不考虑返回值,尽量使用效率高的前置++ (++i)
9、不要使用uint类型,如果需要使用大整型可以考虑int64,否则类型的隐式类型转换会带来很多麻烦
10、如无特殊必要不要使用宏,可以考虑使用const或constexpr替代宏,宏的全局作用域很麻烦,如果非要用在马上要使用时才进行 #define, 使用后要立即 #undef
11、google文档说一定不要用宏来控制条件编译(但是我自己还没有查到不用宏如何控制条件编译,或许就不要搞条件编译)
12、尽可能用 sizeof(varname) 代替 sizeof(type).使用 sizeof(varname) 是因为当代码中变量类型改变时会自动更新. 您或许会用 sizeof(type) 处理不涉及任何变量的代码,比如处理来自外部或内部的数据格式,这时用变量就不合适了
13、类型名如果过长的话可以考虑使用auto关键字
14、注释统一使用 // ,不要通过注释禁用代码,擅用git,不要为易懂的代码写注释
15、写完代码后记得format,VS Code(windows快捷键)shift + alt + F ,每个项目最好都有统一的.clang_format文件。
16、使用C++的string和stream替代C语言风格的char*,使用std::ostream和std::cout替代printf()、sprintf()等
17、尽量使用STL标准库的容器而不是C语言风格的数组,数组的越界访问之类当时是不会报错的,反而可能弄脏堆栈信息,导致奇奇怪怪难以排查的bug
18、可以更多的使用模板元编程,尽量多的使用constexpr等编译器计算,编译器是我们的好搭档,个人认为模板元编程以后会是C++的主流技术
19、可以考虑更多的使用异常处理方式,而不是C语言风格的errno错误码等,这里可以参考你的c++团队还在禁用异常处理吗?
开课吧:分享C++代码的整洁之道!相关推荐
- 四月书单--《你一年的8760小时》,《代码的整洁之道》
当你深刻的认识到自己的不足时,就会激发无限的潜能.成为一个优秀的自己.前几天和发小聊天,她告诉我,一年前想通了一件事,只和过去的自己比较.从农村来到北京的她曾经因为别人的辉煌而不自信,但是庆幸她能告诉 ...
- 3.注释(代码的整洁之道)
3.注释(代码的整洁之道) 目录 注释不能美化糟糕的代码 用代码来阐述 好注释 坏注释 注:代码的整洁之道PDF: https://pan.baidu.com/s/16PLDWPiusGjcUfW_j ...
- 2.函数(代码的整洁之道)
2.函数(代码的整洁之道) 目录 短小 只做一件事 每个函数一个抽象层次 switch语句 使用描述性的名称 函数参数 无副作用 分隔指令与询问 使用异常代替返回的错误码 别重复自己 结构化编程 如何 ...
- 1.有意义的命名(代码的整洁之道)
1.有意义的命名(代码的整洁之道) 目录 名副其实 避免误导 做有意义的区分 使用读的出来的名称 使用可搜索的名称 避免使用编码 避免思维映射 类名 方法名 每个概念对应一个词 别用双关语 使用解决方 ...
- 《Clean Code》代码的整洁之道(一)
<代码整洁之道>:细节之中自有天地,整洁成就卓越代码 概述 软件质量,不但依赖于架构及项目管理,而且与代码质量紧密相关.这一点,无论是敏捷开发流派还是传统开发流派,都不得不承认.<代 ...
- 架构整洁之道 pdf_代码有整洁之道,而架构同样有整洁之道
大家好!我是超级机器人 UltraBot,今天给大家一些值得阅读的开源书籍和项目. Etcd3 学习笔记 etcd 是一个分布式一致性键值存储,用于共享配置和服务发现.etcd 是 Go 编写,并使用 ...
- 前端代码的整洁之道 | 技术头条
在前端开发过程中,你有没有遇到过由于代码交互太多太重时,想改动一行代码"牵一发而动全身":使用框架很爽,可框架绑定应用却很麻烦?那么如何解决呢? 你需要"前端整洁&quo ...
- 《代码整洁之道》(Clean Code)- 读书笔记
一.关于Bob大叔的Clean Code <代码整洁之道>主要讲述了一系列行之有效的整洁代码操作实践.软件质量,不但依赖于架构及项目管理,而且与代码质量紧密相关.这一点,无论是敏捷开发流派 ...
- 代码整洁之道(Clean Code)- 读书笔记
Sorry, 许久未更新文章了,主要因为刚刚换了一家新公司,忙于组建团队(建设.招聘.流程.框架等)与熟悉公司业务,还有领导给的其他工作等等,实在是没有时间更新了.最近在和团队分享Bob大叔的< ...
最新文章
- LeetCode 300. 最长上升子序列(Longest Increasing Subsequence)
- 函数式编程4-高阶函数
- r语言pls分析_R语言:生存分析
- 数据科学 IPython 笔记本 8.17 使用 Seaborn 的可视化
- php用win还是linux系统,做网站选择linux系统还是选择windows系统好?
- linux 6.5 mongdb php扩展插件,linux下为php添加mongodb扩展
- 190627每日一句南京创新周;那些绝对不会失败的人,是永远不去尝试的人
- 手机网络IP地址问题
- Rancher管理k8s集群
- python lncrna_使用CPAT分析lncRNA
- 祝学校计算机系的新年贺词,计算机工程学院 2019祝福情景剧
- max7456 C语言,用于MAX7456随屏显示器SPI接口的C程序
- 医保卡和社保卡的区别
- 控制手柄分体式滑环在船用起重机中的运用
- C#中的异步和多线程
- 根 ssl 2668 优美的暴力
- 铕掺杂的氧化钇纳米片/片状氧化钴二维层状碳化钛复合材料-瑞禧
- Linux如何更改root用户密码
- c++实现的阻塞队列
- c语言运行后tecplot云图,tecplot执行fluent后处理截面云图显示-20210327085801.docx-原创力文档...
热门文章
- [转载] ml-8-1-聚类( ( Clustering) )
- [转载] python查看的统计量_python 描述性统计_Python中的基本统计:描述性统计
- LeetCode 712. Minimum ASCII Delete Sum for Two Strings
- macOs 使用Homebrew升级到MySQL 8系列之后,php无法连接解决方法
- _视图控制对象生命周期-init、viewDidLoad、viewWillAppear、viewDidAppear、viewWillDisappear等的区别及用途...
- 记忆碎片---搭建php+apache+eclipse中的问题
- 解决linux普通用户ls不显示颜色的问题
- Git使用方法(三)-小技巧篇
- python随机森林库_随机森林库:R和Python中的不同结果
- c++中*是什么意思_int在python中什么意思