大型C++项目必须注意的几个小问题

有些问题对于小型的C++项目来说可能无关紧要,但对于大中型C++项目来讲,这些问题却成了大问题。什么样的项目算是小型项目呢,什么样的算是大中型项目呢,我认为10万LOC以下为小型项目,10-50万LOC为中型项目,50万LOC以上为大型项目。当然,不能单纯地以代码行数作为衡量标准,前几天产品重构,我用四二三十行代码换掉了原来的三四千行代码,那这个项目的规模是用这二三十行来计算呢,还是用那三四千行算呢?软件很难有一个准确的度量标准,暂以行数作为一种参考性标准吧。

当项目较大量一些在小型项目中不需要考虑的问题也变得非常重要了,对这些问题还是要认真考虑的。

1. 目录结构
小型项目可能一个目录就够了,把所有头文件和源代码及相关的工程文件放到一起,但项目较在时目录中的文件也就多了起来,文件一多查看就很不方便,如果对各种文件执照功能或类别进行一些分组就会方便很多,比如查找某个文件也很迅速。这一点,我觉得boost和chrome做得是很好的,包括它们的命名规则都很统一,而ACE做得就不是太好,所有文件放到一个目录里,好几百个文件,如果安装了CVS或SVN的windows客户端,在打开资源管理器时还要扫描一下文件是否与版本库关联,使得反应其慢,感觉不是太好。

2. 模块划分
一个大型的项目不可能当成一个整体工程一下子编译完的,如果有几百个文件,使用一个Makefile或者VS的工程,每编译一次都会很耗时,如果需要经常调试的话,编译就会很消耗时间。如果把整个工程划分成多个模块,不同模块以静态库或者动态库的方式进行组织,各模块不仅可以分别编译,单独测试,而且对于多个人协作的并行开发是非常有利的。因为不论是VSS、CVS还要SVN等这些主流的版本管理软件对于并行开发都不能做到尽善尽美,其实这个也不可能,因为软件本身不可能识别程序,像人一样去合并程序代码,如果几个人会经常修改同一个模块的代码时冲突可能就比较多,解决冲突的次数多了不仅影响工作效率,而且还影响心情。

3. 头文件层次
为什么把头文件的层次结构单独分出来而不是直接把模块划分放到同一个问题里呢?就是要突出其重要性。如果在划分模块时没有把模块的组织方式设计合理,很有可能导致头文件循环引用的情况,这样程序就无法编译。

有一些特殊情况就更需要注意,比如windows中的winsock2.h和windows.h存在冲突,如果先引用windows.h再引用winsock2.h就会出现重定义的情况。如果想使用Win32 Socket API 2,则必须先引用winsock2.h然后引用windows.h,可是如果在多个文件里引用windows,如果顺序控制不好就有可能导致混乱,从而程序无法编译。个人认为最好的方法就是把该引用放入一个头文件,比如叫sysinc.h,然后所有编译单元的头文件都在第一行引用sysinc.h,然后所有的.cpp文件在第一行引用对应的头文件,这样即可保证winsock2.h和windows.h的引用顺序。

4. 命名空间
C语言是不支持命名空间的,以前人们为了避免标识符冲突都是加前缀或者后缀,但这样会使得标识符很长,看起来不舒服,而且写起来很麻烦(虽然现在IDE对智能感知的支持已经使这个变得很方便),还老感觉怪怪的,不够自然。所以,后来的语言都对命名空间(或类似功能)进行了支持,如C++、C#、Java、Python等,所以在大型的C++项目为什么不用这个新的特性呢?

使用不同命名空间中的标识符识不要图省事使用using namespace ns; 这样的语句把整个ns命令空间内的标识符都引进来,这样便失去了命名空间的作用。因为这样做可行是需要一个前提的,就是知道这个不会冲突,可是谁能预知未来不会冲突呢?前些天就遇到在WINDOWS、Linux、AIX三个平台编译都没有问题,可是到了Solaris就出了问题,原因是在系统的头文件if.h中有一个结构定义struct map,这个跟C++标准库中的模块map冲突,导致无法编译。

在这方面,boost的作者们不愧是C++的专家,这方面做得很好,而ACE就不是太好,所有标识符通过前缀ACE_来标识,保必呢,而且是大写,看起来很不舒服,如果要用呢只能忍了,或者自己单独写一个头文件,使用typedef或者用宏定义把标识符都给改了,然后把新定义的标识符封装到一个ace命令空间中,这也是个不错的主意。

5. 代码注释
时间长了谁也难保证记住每一段代码的用途,特别是一些特别技巧,一定要随手写下来,为以后自己维护或与同事合作都是很必要的,不要怕别人看懂自己的代码对自己有威胁,开放一些,只有互相学习才能促进发展,你把代码给了他人,他人也会给你建议帮你进步的,中国前一百年的历史就是固步自封的沉痛教训,国家如此,个人亦如此。

6.版本管理工具
如果几个人同时维护几十万行代码都没有使用版本管理工具的话,那你每天肯定只能有一种感觉,那就是在浪费生命。浪费自己的生命也就算了,如果作为一个领导者不对此做点改变的话,那就是浪费别人的生命,也就是谋财害命。

7. 代码规范
每个人可能都有自己的编码风格,但是同一个项目,应该只有一种风格,否则可能会影响工作,虽然程序本身的性能和功能都没有会有问题,但对于产品的实际开发过程会有影响,因为它会影响到一些有想法的工程师的心情,当然就会影响工作效率,因为他们老感觉那些代码不够规范,总想去修改这些细节,当改过后可能会被另外的工程师改成另一种风格。所以必须有统一的风格,以统一团队每一位成员的行为。

有一个细节,我还是有体会的。项目中一些旧的代码写得很乱,有的一个函数都能写几百行,而且代码块的花括号{ 和}都是另起一行if-else都是在单独的行,这样着满屏都是花括号和if - else,看起来代码很不紧凑,比如:
if( cond 1 )
{
line …
line …
}
else
{
line …
}
改成
if( cond 1 ){
line …
line …
}else{
line …
}

看起来就紧凑多了。
还有一些代码层次很深,一块代码可能都有四层if条件嵌套,看起来是很费劲的,如果在每一次条件判断都进行退出的话,可读性将会有很大的改善,如
if( cond 1 ){
if( cond 2 ){
if( cond 3){
work line
}
}
}
如果改成下面代码,看起来会更舒服些
if( ! cond 1)
return –1;
if ( ! cond 2 )
return –2;
if( !cond 3 )
return –3;
work line

当然写成
if( cond 1 && cond 2 && cond 3 ){
work line
}
更好,但有些时候并没有这么方便。

8. 自动化构建
如果一个大型项目包含几十个小模块,每次都要手工编译依赖项的话,工作效率是低下的,如果能够自动构建必要的模块将可以大大提高工作效率,即使每次能少输入几个字符也是必要的。
比如我们的一个项目需要在windows、Linux、AIX、HPUX、Solaris五个平台上运行,如果每次编译前都执行configure,然后再执行make,也不是很方便,如果公共的部分写到一个Makefile.comm中,然后为每一个平台的平台依赖项分别写一个Makfile.win,Makefile.linux, Makefile.aix,Makefile.hpux, Makefile.solaris,这样每次只需要输入make –f Makefile.os来编译,但这样要输入的要挺多的,可以再建一个Makefile文件,然后自动检测系统平台,然后调用对应的Makefile,以后只需要输入make就行了,再想简单,可以写个小脚本,文件名为m,里面调用make指令,这样每次只需要输入./m即可,如果还嫌输入的字符太多的话,可以把.加到你的PATH变量中,以后直接输入m就可以了。如果再想简单呢?那只能使用特异功能,玩儿心灵感应了,我实在是想不到其它的办法了。
只是个例子而已,可能实际开发过程中需要更多其它的工作,但当你发现你做某件事时不再是第一次,你就可以考虑下次遇到后如何简化了,这也是“程序修炼之道”中提到的DRY(Don’t Repeat Yourself)法则。

9. 自动化测试
对于一个大型项目,每次构建后都进行手动测试的工作量是很大的。如果公司没有研发团队配备测试人员,而且测试部不能给及时测试的话,研发力会将会有大量的人力浪费在测试上,所以构建自己的自动化测试系统是一项至关重要的内容。

暂时谈到这儿吧,想到再记下来,有机会与网友探讨,各位晚安。

大型C++项目必须注意的几个小问题的更多相关文章

  1. 加速 Gradle 构建大型 Android 项目的方法[转]

    加速 Gradle 构建大型 Android 项目的方法 时间 2016-03-14 20:38:00  Mystra 原文  http://www.wangchenlong.org/2016/03/ ...

  2. Gradle在大型Java项目上的应用

    在Java构建工具的世界里,先有了Ant,然后有了Maven.Maven的CoC[1].依赖管理以及项目构建规则重用性等特点,让Maven几乎成为Java构建工具的事实标准.然而,冗余的依赖管理配置. ...

  3. 15套java架构师、集群、高可用、高可扩展、高性能、高并发、性能优化、Spring boot、Redis、ActiveMQ、Nginx、Mycat、Netty、Jvm大型分布式项目实战视频教程

    * { font-family: "Microsoft YaHei" !important } h1 { color: #FF0 } 15套java架构师.集群.高可用.高可扩展. ...

  4. java架构师负载均衡、高并发、nginx优化、tomcat集群、异步性能优化、Dubbo分布式、Redis持久化、ActiveMQ中间件、Netty互联网、spring大型分布式项目实战视频教程百度网盘

    15套Java架构师详情 * { font-family: "Microsoft YaHei" !important } h1 { background-color: #006; ...

  5. Java 架构师+高并发+性能优化+Spring boot大型分布式项目实战

    视频课程内容包含: 高级 Java 架构师包含:Spring boot.Spring cloud.Dubbo.Redis.ActiveMQ.Nginx.Mycat.Spring.MongoDB.Zer ...

  6. 入职一个月快速熟悉大型Vue项目经验感想

    来到和睦的公司家庭已经一个月出头了,从技术层面来说,公司项目PC端是我目前来说接触的最大最复杂的项目了,德老师也说这个不断开发更新迭代的项目的代码量相对于全国的web来说是蛮多的,对于快速熟悉这样的大 ...

  7. 大型可视化项目用什么工具好呢?——不如了解一下阿里云DataV尊享版

    随着信息化的发展和进步,可视化大屏开始为社会各行业提供全面应用.目前越来越多的需求显示希望大屏能够更直观的还原出所要展示数据可视化的真实场景,让整个项目更立体.更有科技感,让项目在面对复杂操作时能灵活 ...

  8. 蒲公英 · JELLY技术周刊 Vol.13 跟 VSCode 学习如何开发大型 IDE 项目

    开发一个 IDE 很难么?这或许是件很难的事情,但当我们参考 VSCode 的技术构架来看,整个开发流程就会平滑顺畅很多,从内核开发.代码编辑器.视图结构到插件系统,在这整个技术构架中我们可以看到很多 ...

  9. IT咨询顾问:一次吐血的项目救火 java或判断优化小技巧 asp.net core Session的测试使用心得 【.NET架构】BIM软件架构02:Web管控平台后台架构 NetCore入门篇:(十一)NetCore项目读取配置文件appsettings.json 使用LINQ生成Where的SQL语句 js_jquery_创建cookie有效期问题_时区问题

    IT咨询顾问:一次吐血的项目救火   年后的一个合作公司上线了一个子业务系统,对接公司内部的单点系统.我收到该公司的技术咨询:项目启动后没有规律的突然无法登录了,重新启动后,登录一断时间后又无法重新登 ...

随机推荐

  1. 关于PHPExcel 基础使用方法

    $dir=dirname(__FILE__);//找到当前脚本所在路径require_once $dir.'/PHPExcel/PHPExcel.php';$objPHPExcel=new PHPEx ...

  2. ios 苹果内购订单验证 --- nodejs实现

    实现代码 function IosPlayVerify(data,orderid,cb) { itunesPost(data,function (error,responseData) { if (e ...

  3. 时间轮算法的定时器(Delphi)

    源码下载 http://files.cnblogs.com/lwm8246/uTimeWheel.rar D7,XE2 编译测试OK //时间轮算法的定时器 //-- : QQ unit uTimeW ...

  4. python之获取微信好友列表并保存文档中

    代码如下 from wxpy import * from pprint import pprint #登录微信 bot = Bot() my_friend = bot.friends() f = op ...

  5. Python学习第一弹

    开发语言: 高级:Python.java.PHP  C#   GO  ruby   C++           ——>字节码   低级:C.汇编                          ...

  6. 学习Pytbon第十七篇,面向对象编程

    面向对象技术简介 类(Class): 用来描述具有相同的属性和方法的对象的集合.它定义了该集合中每个对象所共有的属性和方法.对象是类的实例. 类变量:类变量在整个实例化的对象中是公用的.类变量定义在类 ...

  7. 插入排序算法Java实现

    一. 算法描述 插入即表示将一个新的数据插入到一个有序数组中,并继续保持有序.例如有一个长度为N的无序数组,进行N-1次的插入即能完成排序:第一次,数组第1个数认为是有序的数组,将数组第二个元素插入仅 ...

  8. js按钮点击事件

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  9. Android面试收集录9 IntentService详解

    一. 定义 IntentService是Android里面的一个封装类,继承自四大组件之一的Service. 二.作用 处理异步请求,实现多线程 三. 工作流程 注意:若启动IntentService ...

  10. hover 改变另一个标签的属性