C++项目开发指导(新员工培训材料)

        (注:这是一份给新员工的培训材料,集合了实际工作的经验和教训,不一定具有普适性。这份东西大概写于2014-1016年,不涉及之后的社会新气象。特别需要强调的是,这是面向新员工的培训,重点在于破除学习阶段形成的幻觉、了解工作和学习的不同。本文中所有“注”都是本次发表时增加的

目录

一、序-学以致不用

二、软件工程

2.1软件工程的目标和手段

2.2最基础的要求:让其他人看懂

2.3即使没有过程管理

三、可维护性

四、开发环境与工具

 4.1花一些时间学习工具软件的高级特性

4.2使用版本管理软件

4.3没有工具可用时:用最原始的方式工作

4.4常用工具软件

4.5基本的工作方式

五、兼容性

5.1主机差异

5.2编译器差异

5.3版本差异

5.4第三方软件

六、编码

6.1遵循编码规范

6.2首先考虑可读性,然后再优化

6.3多花精力在代码结构设计上

6.4避免使用不兼容的代码

6.5记住:出错处理是编码中最大的工作量

6.6用Interface的标准设计类

6.7避免使用C++的降低可读性的特性

6.7.1避免使用IO流

6.7.2避免使用异常

6.7.3避免使用同名函数-全项目范围

6.7.4避免使用单参数构造函数

6.7.5避免使用运算符重载

6.7.6避免滥用类继承

6.8保留测试代码和数据

七、继续学习的指引

7.1自行学习的书籍

7.2编程规范

7.3具体项目的开发指南


一、序-学以致不用

        编程这种活计,是一种技术活,是非常依赖经验的。经验之中最重要的经验是,最好的技术不是最炫的技术而是最熟悉的技术。

        作为商业软件的开发者,编码的目标是为了实现需求、更好地实现需求以及有限地实现未来的需求。

        “学习”不是商业软件开发本身的内容。“学习”包括但不限于这些东西:最新版本的C++语法(比如C++11),时下时兴的开发库(比如ACE,曾经时兴),最新的开发工具,等等。

        “学习”当然是非常重要的事情,但是只有少数人明白,“学以致不用”是比“学以致用”更高的境界。

        不同的技术有不同的适用场合,不同的项目有不同的适用的技术(放弃所有不适合的技术)。脱离具体要求的“最佳”技术是不存在的。

        必须不断地学习才会获得在不同的技术中做出选择的能力。

        技术人员追求的境界是“广”和“专”,但是只有极少数人可以同时做到广和专。

如果没有能力做到“广”,就请“专”。如果没有能力做到“专”,请改行。广而不专做不了程序员。

        我们的绝大多数项目都是工程性项目,就是按照特定的要求开发一套东西然后部署在给定的硬件之上最后持续地提供支持。

        具体项目有具体的硬件和软件条件,程序员大多数时候没有选择权,只能在给定的硬件和软件条件下编程。

        大多数项目需要在多个平台下运行,最好从一开始就考虑兼容问题,后期再考虑移植是个噩梦。

        学院里倾向于研究最新的技术,但是在我们这里,不得不为了兼容性而放弃很多新特性。

        通俗地说,就是一句话:学习和生产是两回事

二、软件工程

2.1软件工程的目标和手段

        目标:交付代码正确的符合功能和性能要求的软件

        手段:文档、测试和评审

        

        测试已经能够验证代码是否正确(无BUG)、功能和性能是否符合要求,为什么还需要评审呢?这是因为测试只能验证最终结果,不能保证开发过程顺利,也不能保证代码质量,更不能代码的可维护性。

         对于一个孤立的项目,可维护性可能不是短期目标的一部分,但是对于绝大多数情形而言,软件交付以后都是需要维护的,因此可维护性是很重要的。

        对于一个软件开发组织而言,不同项目总是有各种可以借鉴、重用的地方,缺乏良好组织的代码很难被其他项目利用。

        评审包括对各阶段文档的评审和对代码的评审。即使一个软件开发组织完全是无管理的开发,一个优秀的程序员也会自己留下一些文档,同时也会不时地修改自己的代码,而且一定会留下一些自己的可重用的代码(据说每一个程序员都会有一个自己的socket类)。

2.2最基础的要求:让其他人看懂

        对于代码的要求有很多:正确、高效、节省空间、可靠、易读。

         这些要求多数是是互相冲突的。

         “正确”当然是个首要的前提,然而并不容易实现,“没有BUG”早已是个传说。

        “高效”和“节省空间”通常是对立的,我们的系统的很多部分对这两方面要求都追求极致。这要求很高的编程技巧,同时也带来复杂难懂的代码。

         “可靠”的代码不会犯错,能正确处理各种罕见情形,必然会变得冗长低效。

         “易读”的代码是别人能够看懂的代码,“高手”写出的代码固然可能是晦涩难懂的,然而“低手”缺乏正确逻辑和设计的代码才是最难以理解的。

         我个人认为代码的可读性是首要的要求,因为破坏可读性的因素最常见的因素就是糟糕的代码结构、不自然的功能划分,这代表程序设计能力低下。

         良好的设计是完成“正确、高效、节省空间、可靠、易读”的基础,而设计能力是各种编程技术和逻辑分析能力的综合。

2.3即使没有过程管理

        即使没有严格的过程管理……(注:我诚实地口头告诉他们“我们的CMM证书跟你们无关”

        仍然应该尽可能向软件工程靠拢,尽量提高开发质量。

        尽可能多地留下一些“有价值”的文档。

三、可维护性

        由于公司绝大多数项目都是开发、维护一体的,甚至直接参与运营,因此项目的可维护性非常重要。

         可维护性除了靠良好设计来保证之外,代码的可读性是对编码人员的最基础的的要求。

         我们建议把可读性放在“高效”和“节省空间”之前,作为优先原则。

四、开发环境与工具

        (注:这部分与具体企业和项目关系很大,越是大项目,历史包袱越重

 4.1花一些时间学习工具软件的高级特性

        现在的开发工具越来越强大,也需要越来越多的学习时间,但是相对于更强大的工具带来的高效,学习工具所需的时间并不算多,特别是像VS这样以容易使用著称的软件。所谓磨刀不误砍柴功,VS肯定是一把好刀。自从VS2010以来,不再需要插件便可以轻松发觉所有的语法错误,基本上都能一次性编译通过(当然,逻辑问题是工具发现不了的)。

        多数软件都有很多高级特性用来方便用户,不要满足于基本的使用,花时间研究一下工具软件的高级特性能够很大地提高生产力。

        越是功能强大的软件界面越是复杂,于是很多人宁愿使用简单的工具,好吧,这算价值观不同吧。

4.2使用版本管理软件

        我们所用的版本管理软件是SVN。

        不论你的项目是否使用版本管理技术,你都应该为自己使用一个独立的版本管理软件。如果你觉得你的历史代码是无关紧要的,你不用继续往下看了。

        有时候修改代码过于复杂或者具有尝试性,为了避免使项目的正式版本库过于混乱,你可能会考虑只在最终完成以后才提交给项目正式库,之前只在自己的私有库里面提交。

        你还会有一些私人的代码,比如自己的练习,有个版本控制比没有版本控制好。

        VSS和SVN是两个互不干扰的版本控制软件,可以同时在相同的目录下工作。

        VSS和GIT也互不干扰。GIT和SVN是否互不干扰有待考察。

        VSS在win10正式版中仍然可以良好运行,虽然需要下载较早的.net2.0库。

        (注:VSS-Visual SourceSafe已经彻底淘汰了,现在免费版本管理的主流就是SVN和GIT

4.3没有工具可用时:用最原始的方式工作

        具体项目的开发环境受制于具体的硬件和软件条件,有时候主机上没有安装调试工具,或者程序没有用调试开关编译,或者程序太复杂以至于使用断点调试非常困难,只能通过增加调试代码来发现问题。

        调试工具dbx或者gdb有时候很有帮助,但是并不是必须的。

        目前的系统中一直存在一个全局的调试开关,可以动态地打开或关闭调试代码。对于我们这种边生产边维护的系统,使用宏断言的机制并不适合。

4.4常用工具软件

        (注:这部分时代感很强,当时去IOE尚未完成

        推荐使用的编程工具是Visual Studio 2010以上版,免费的express版就足够了,毕竟我们只是编辑代码。借助一些插件,其他一些源码编辑软件也可以胜任。但是,过去几年来,最好的C++编码工具一直是Visual Studio,没有之一。

        UltraEdit,快速查看和修改代码的工具,可以直接打开ftp服务器上的文件。借助插件也有很强大的能力。

        vi,UNIX上的文件编辑命令,最直接的工具,仅在需要立即查看和简单修改代码的情况下使用。维护人员可能需要用这个。完全可以用CuteFtp的编辑功能代替。

        CuteFtp,FTP工具,注意传输模式,用错了传输模式会导致文件错误。

        telnet,windows自带的telnet工具,足够强大,但是很多人仍然喜欢其它的telnet工具。

        SecureCRT,连接linux的工具。linux通常不安装传统的telnet服务器,而是安装更安全的终端服务(ftp也换用了安全的ftp,不过CuteFtp都支持)。

        TortoiseSVN,SVN客户端。SVN是上级母公司规定的版本管理工具。

        另外还需要oracle客户端和开发工具,大多数人用的是TOAD或者SQL Developer,前者适合C++程序员,后者适合数据库开发人员(以编写PL-SQL为主要工作)。

4.5基本的工作方式

        (注:哈哈哈哈,我们相当得原始

        SVN管理版本

        windows编辑

        ftp上传

        telnet编译运行

五、兼容性

        我们的程序可能在四种机型上运行:IBM、HP、SUN的小型机和运行Linux的PC-SERVER,由此引发了普遍性的兼容性问题。

         主要系统最初在三种小型机上运行,后仅在IBM上运行,目前正在试图移植到Linux上。

         小型项目最初可能只在一种机型上运行,然而经常会在后来要求移植到其他机型上去。

         在最终完成全部迁移到linux以前,兼容性一直是必须考虑的。

5.1主机差异

        主机的差异具体体现在下面几个方面(STL的差异属于编译器差异):

        系统头文件不同

        函数参数不一致(比如文件和网络函数)

        函数功能细节不同(比如信号函数)

        函数不同(linux通常有较多新函数)

        支持的信号不同

        动态链接实现机制不同(问题主要出在IBM上)

5.2编译器差异

        由于主机机型不同,编译器也就不相同(理论上g++也可以安装在三种小型机上,但是没有这样做,理由很多)。IBM和HP是老牌厂家,他们的编译器缺乏对C++新特性的支持,STL也是很老的版本,甚至不支持allocator的一些特性(在SUN上开发的代码拿到IBM和HP上无法编译,导致一项很有价值的升级失败)。

        编译器的差异体现在下面几个方面:

        编译命令和参数不同

        标准头文件不同(不带后缀名的头文件)

        库交叉依赖支持性不同(g++不支持两个库交叉依赖)

        部分类已经废弃(strtream等)

        容器的内存特性不同(如vector最少申请1024个空间,导致极大的内存)

        模板新特性不支持

        部分语法差异(for变量声明差异,typename的使用,using的使用)

        警告不同(由于多数警告确实意味着潜在的BUG,建议在所有平台打开所有警告)

5.3版本差异

        同一个厂家的机型因为系统版本不同也会有所差异,目前已知SUN和HP新机型的编译参数与旧机型不同,不过这问题不大,只需要修改一个配置文件即可。

5.4第三方软件

        谨慎使用第三方软件,由于前面所述的兼容性问题,第三方软件可能是一个隐藏的炸弹——当需要移植的机型不支持这个第三方软件的时候,这可能造成灾难。

        第三方软件也会给其他的维护人员造成困难。

        借助开源软件永远是一个最有诱惑的解决方案,但并不总是正确和容易的方案(版权问题另议)。

六、编码

6.1遵循编码规范

        所有的编码规范的内容都包含代码格式、避免易错代码、避免低效代码几个部分,大多数都应该被遵守。

        的确存在一些编码规范是有争议的,这是需要结合实际情况考虑的。

        C++标准本身就存在巨大的争议,是追求代码漂亮、高效还是追求不易出错,不同的人有不同的选择。

6.2首先考虑可读性,然后再优化

        后期改善可读性的难度很大。实际上,如果代码可读性很差,优化的难度也很大。

6.3多花精力在代码结构设计上

        好的代码结构设计是可读性的基石。浅显的流水账代码当然也是“可读”的,但是你很难读到有价值的信息。

6.4避免使用不兼容的代码

        即使当前只为单一机型开发。

        宁愿使用朴实一些但是兼容的代码而不使用最新的精彩的特性。

        尽量使用老版书籍而非新版,这是一个无奈的选择……在我们仍然使用IBM的时候。

6.5记住:出错处理是编码中最大的工作量

        一个一行的函数调用也许需要20行代码来处理各种不同的出错情形,即使时间紧迫,无论如何,至少要判断函数返回是否正确。

        顺便的,尽可能把函数设计为返回bool值,可以避免写错了判断代码。

6.6用Interface的标准设计类

        总是用接口标准来设计类能使你抓住设计的精髓——接口的合理性而不是内部实现。

        从接口的角度来思考能使你正确地划分类和层次。

        经验表明,草率的设计迟早会被发现问题,而良好的设计给重用和重构带来极大的便利。

6.7避免使用C++的降低可读性的特性

6.7.1避免使用IO流

        (注:这里主要谈的是数据输入,因为系统主要是处理文件,至于输出,cout是不可能不使用的

        虽然IO流是C++课程的重要一部分,IO流在实际软件开发中的价值却不大,除了日志输出这样没什么细节要求的场合,大多数输入输出都不是IO流发挥作用的场合。

        IO流处理格式化输入输出的问题在于,格式错误是程序必须处理的部分,而IO流不能处理格式错误。

        绝大多数程序中的输入处理方式是按行读取,然后分解,判断格式是否正确。

6.7.2避免使用异常

        进入C++标准的是exception而非interface是个悲剧。

        异常的支持者认为应该使用异常,但是很少有人能够合理地使用异常,而且异常导致不能仅仅通过上下文顺序寻找来理解代码,这对于代码维护是一堵墙。

6.7.3避免使用同名函数-全项目范围

        当需要替换一个函数或者改变一个函数的行为的时候,为了快速发现函数的影响范围,立即搜索到函数的所有调用位置是很重要的,此时你会立即意识到本条建议的价值。

6.7.4避免使用单参数构造函数

        单参数构造函数会导致自动类型转换,这可能引起及其晦涩的BUG,特别是系统中大量存在与string的自动转换时。此问题曾经在系统中引起了一个BUG,费了很大劲才解决。

6.7.5避免使用运算符重载

        运算符重载有助于写出简洁的代码,但是出问题的时候很难快速查出函数被调用的地方,因为完全无法使用文本搜索功能。

        目前开发工具的发现函数调用点的功能很糟糕,所以最好还是避免使用无法用文本搜索来发现的函数(建议避免使用IO流也有同样的原因)。

6.7.6避免滥用类继承

        继承被毫无疑问地滥用了,绝大多数使用继承的场合应该使用“包含”,一小部分应该直接修改基类,只有被称为接口的才是正确的使用继承的场合。

6.8保留测试代码和数据

        写程序的时候总是要自我测试,测试用的代码和数据都不应该丢弃,而应该作为项目产出的一部分,毕竟,修改和维护是不可避免的。而且,在我们这里,通常维护人员还是你自己。

七、继续学习的指引

7.1自行学习的书籍

        一本关于UNIX/LINUX编程的书

        一本关于UNIX命令和shell的书

        一本C++教材

        一本C++代码设计的书

        一本面向对象设计的书

7.2编程规范

        几份不同的编程规范,由于并未严格实施,因此都可作为参考。

7.3具体项目的开发指南

        不同的项目有不同的公共库和开发习惯,这是我们正在努力统一的。

(这里是结束)

你可能感兴趣的:(软件工程,c++,开发语言,软件工程,员工培训)