用c++开发一款商业产品,需要哪些规范

最近开始接手项目组的开发管理工作,项目组开发的产品一期功能基本开发完成,进入内部测试及小渠道发布阶段,然而产品的稳定性还存在很大问题。

先做一下背景介绍,项目组开发的是一款面向C端的互联刚产品,运行操作系统为windows。整个项目使用c++开发,总体代码量大概在数十万行。

c++是一门很复杂的语言,有很多强大的特性,然而当用其开发一款商业产品时,这些特性可能会带来麻烦。所以当设计c++的使用规范时,更多的是对其做减法。

本文的规范针对VC++开发环境,开发工具为Visual Studio。

文件系统目录规范

一款完整的商业产品开发通常会涉及到很多模块,这其中包括可执行程序(.exe),项目组开发的库(静态库或动态库),第三方的库(静态库或动态库),测试程序,这么多的模块和代码,需要一个良好组织的目录结够。
这里假设项目名称为XXProject

  • XXProject
    • XXProject.sln
    • Bin
      • Debug
      • Release
    • TestBin
    • Debug
    • Release
    • Src
    • Document

其中Bin存放需要发布的可执行程序,TestBin存放测试程序的可执行文件,Src存放项目的工程文件和源代码,Document存放项目相关的开发文档(如项目说明,代码规范等)。

接下来假设项目包括如下工程:XXMain(发布的主程序),XXUpdate(升级程序),XXSdk(自己开发的基础库),XXThird(第三方库), XXTest(测试程序)

  • Src
    • XXMain
    • XXMain.vcxproy
    • code文件
    • XXUpdate
    • XXUpdate.vcxproy
    • code文件
    • XXTest
    • XXTest.vcxproy
    • code文件
    • XXSdk
    • XXSdk.vcxproy
    • code文件
    • ThirdLib
    • XXThird
    • Lib
    • Debug
    • Release
    • Include
      Lib库用来存放静态库,动态库的.a文件,Include用来存放公共头文件,ThirdLib用来存放第三方库。

解决方案目录规范

解决方案目录是VS开发工具提供的逻辑上组织项目的方式,与物理文件系统并不存在对应关系。
仍然假设项目包含上述项目和模块。

  • Solution (解决方案)
    • Application (解决方案文件夹)
    • XXMain (工程)
    • XXUpdate (工程)
    • Test (解决方案文件夹)
    • XXTest (工程)
    • Library (解决方案文件夹)
    • XXSdk
    • ThirdLibrary(解决方案文件夹)
    • XXThird
    • Public (解决方案文件夹)
    • 公共头文件

代码编写规范

1:禁用全局变量

全局变量会带来晦涩的依赖问题

2:禁用goto指令

goto指令的代码难以阅读和维护

3:禁用异常机制

c++的异常机制有很多缺陷且复杂

4:用struct封装数据,使用class定义对象

C++中class和struct几乎没有区别,在规范中进行语义的区分

5:struct和class必须显示包含构造函数
6:除非特殊情况,总是将析构函数定义为虚函数

方便继承时的资源释放

7:不要在构造函数中执行复杂操作,推荐加入init函数用于初始化操作

构造函数没有返回值,难以反馈错误

8:不要在构造函数中调用虚函数

构造函数中的虚函数不会重定向到子类。

9:慎用继承

相比对象组合,继承带来更强的依赖,推荐使用接口继承而不是对象继承

10:禁用多重继承(接口继承除外)

多重继承通常代表不好的设计

11:慎用运算符重载

运算符重载会混淆代码的语义,应只在不会造成混淆时使用

12:将成员设置为私有并提供访问函数

封装是降低代码耦合的有力武器

13:将同一访问权限的成员定义在一起

可以按照public,protect,private顺序进行组织

14:避免出现大而全的类

当一个类的代码超过1000行,应有所警惕,超过2000行,则应考虑拆分(行数不包括注释)

15:头文件应包含它所需要的头文件

这样可以保证cpp文件引入该头文件后不需要包含其它头文件

16:合理的组织引入的头文件,不要重复引入,不要引入不必要的头文件

可以以系统头文件,第三方库头文件,项目组库头文件,本程序头文件来组合,不同类型头文件之间用空格隔开

17:头文件使用#define宏来避免多重包含

pragma once 指令只有VC编译器能识别

18:允许合理的使用友元特性

19:使用引用传递对象类型参数, 对于不需要改变的参数加入const修饰符

引用传递可以避免对象拷贝

20:函数应该进参在前,出参在后

21:使用明确的返回值指示函数的运行结果,而不是用返回的内容来指示结果

推荐: int GetDeviceName(string& deviceName);
不推荐: string GetDeviceName()

22:声明基本类型变量后立即赋值

正确 int nCount = 0; bool bSuc = false; 错误 int nCount; bool bsuc;

23:使用内联,枚举,常量来代替宏

宏的使用有很多弊端,应尽量避免

24:使用singleton模式代替静态类

相比静态类,singleton模式可以更好地控制初始化时机。

25:使用share_ptr来管理指针

指针和管理在复杂项目中十分困难,使用智能指针是不二选择

26:使用weak_ptr来处理循环引用
27:明确对象或资源的生存周期

明确对象的生存周期通常代表着良好的设计

28:合理的使用缩进,空格

最重要的是保持风格的统一,自动生成的代码可能会打破这种统一,应该灵活设计规则

29:合理使用typedef缩减类型的长度,合理使用auto

使用stl时经常会导致过长的类型,合理使用auto可以有效减少代码长度

命名规则

由于是在VC环境下开发,沿用微软的命名驼峰命名法

选择哪种命名方式实际上不是很重要,最重要的是保持统一

  • 代码文件: DeviceMgr.h, DeviceMgr.cpp
  • 解决方案目录: NetLibrary
  • 工程筛选器: DataModel
  • 类:CDeviceMgr
  • 结构体: DeviceInfo
  • 变量: listDevice
  • 类成员: m_deviceName
  • 函数: GetName; GetDeviceName;
  • 代表bool含义的变量: 都以b开头,如:bOk, bSafe
  • 代表整数含义的变量: i表示符号整数,n表示无符号整数
  • 避免无意义的变量名和缩写: 如 x,dn(deviceName)等

注释规则

  • 文件注释:注明文件作者,联系方式,文件代码作用,重大修改记录等
  • 类注释:说明类的作用,使用限制等等
  • 函数注释: 尽量依靠意义明确的函数命名而不是依靠注释,说明函数的使用限制,对于意义不明确的参数加以说明
  • 变量注释:尽量依靠意义明确的变量命名而不是依靠注释,特殊情况。
  • 实现注释: 对于使用了非常规技巧,或复杂算法,或很复杂的业务逻辑部分要加入注释说明

原则: 注释应风格统一,简短而意义明确,最终目的是有效帮助其它人阅读和理解代码的目的

日志

日志的打印十分重要,是产品发生问题时重要的参考依据

  • 日志的打印要尽量详尽,合理划分日志等级,一般为Fatal, ERROR, WARNING, DEBUG, INFO
  • 统一使用unicode(utf-16)编码来输出日志
  • 提供异步打印日志的接口
  • 提供定期清理日志的机制
  • 在发布版中将日志等级设置为ERROR或更高,提供配置文件供调整日志等级

一些其它规则

  • 避免大而全的类(代码控制在1000行以内)
  • 避免过长的函数(代码控制在200行以内)
  • 避免深层的嵌套(不要超过3层)
  • 使用do-while-break技巧来避免重复写释放资源的代码
  • 尽量使用RALL技巧来释放资源
  • 当函数或代码废弃时,应与标注,最好将其注释掉并定期清理

线程和锁

复杂的项目肯定会涉及到多线程开发,而开发多线程程序是十分困难的。据我们统计,项目组产品有70%左右的崩溃和bug和线程有关。

将线程模块独立出来,交给项目最有经验的开发人员管理和维护,对外暴露抽象接口,屏蔽线程的概念。
强制使用RALL技术来使用锁

未尽
本文并没有涉及到C++规范的所有方面,欢迎讨论和补充

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,378评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • After our more-than-10-hour flight, we successfully lande...
    doooodles阅读 250评论 4 1
  • 老生常谈的一个话题,一个想当年的丑闻,给青岛抹黑的一个片段。 但是,既然出了这样的负面消息,虽然时间过去那么久了,...
    烦人的昵称阅读 232评论 0 0
  • 鄙人姓瀟,名柒泩,[化]字嘛……很不巧我也不知道,自幼习武,琴棋书画,吹啦弹唱,样样精通……停停停!不扯了不扯了,...
    画凉V泩阅读 228评论 1 0