编程之雅(C++编程准则)

来源:互联网 发布:销售数据采集通知 编辑:程序博客网 时间:2024/04/29 23:45

这些编程准则有一些是个人的编程经验,当然,我可没那么多经验,大部分都是各个大师总结出来的,我就是整理一下。

宏观:

  • C++视为C、面向对象C++、模版C++STL C++组成的语言联邦。
  • 任何人不得添加任何东西到STL命名空间
  • 不要轻易忽略编译器的警告
  • 一定程度的使用测试驱动的开发方法
  • 软件实体(类、模块、函数)应该是可扩展的,但是不可修改的
  • 多采用敏捷的设计方法(个体和交互胜过过程和工具、可以工作的软件胜过面面俱到的文档、客户合作胜过合同谈判、响应变化胜过遵循计划)
  • 经常性的交付可以工作的软件,交付的时间间隔越短越好
  • 在整个项目开发期间,业务人员和开发人员必须天天都在一起工作
  • 围绕被激励起来的个人来构建项目
  • 在团队内部,多进行面对面的交流
  • 提倡可持续的开发速度
  • 使要构造的系统最简单(不要设计不需要的功能,不要过分设计)
  • 最好的架构、需求和设计出自于自组织团队
  • 每隔一段时间,团队会在如何才能更有效的工作方面进行反省,然后相应的对自己的行为进行调整
  • 结对编程是一种比较好的选择
  • 不能容忍重复的代码
  • 持续的对代码进行重构
  • 要做计划游戏
  • 高层模块不应该依赖于底层模块。二者都应该依赖于抽象
  • 尽可能的保证:抽象不应该依赖于细节,细节应该依赖于抽象(任何变量都不应该持有一个指向具体类的指针或引用;任何类都不应该从具体类派生;任何方法都不应该覆写它的任何基类中已经实现了的方法)
  • 每个编程单元尽可能的向使用者提供使用承诺:例如资源回收保证、数据一致性保证、无异常保证
  • 尽可能的在程序中处理所有可能的异常,而且尽可能的精细。(try...catch)
  • 应该让程序体面的退出:在出现非计划内问题时自动产生dump文件。(利用SetUnhandledExceptionFilter调用MiniDumpWriteDump
  • 要先设计好类,建好各个类的文件,才能写代码。
  • 用pragma once代替h文件头

类:
  • 让接口容易被正确使用,不易被误用
  • 设计class犹如设计type
  • 将成员变量声明为private
  • 尽量不要让类支持隐式类型转换
  • friend成员函数是类接口的一种表现方式,但能避免使用就尽量避免
  • 避免使用handles指向对象内部成分
  • 慎重使用inline(小函数的确该用inline,但是考虑到inline函数无法调试,所以应谨慎)
  • 确定public继承表现出is-a关系(即Liskov替换原则,永远可以用派生类取代基类)
  • 避免覆盖继承而来的名称(基类函数重载,派生不重载,则其他覆盖;派生重载基类函数,则基类函数覆盖;变量也可以覆盖)
  • 区别接口继承和实现继承(纯接口(virtual=0),接口+朴素实现(virtual=0+实现),接口+强制实现(non-virtual),以上为public继承,private继承全部是为了继承实现,而不继承接口)
  • 根据上一条,只要出现virtual,就尽可能让它=0(成虚基类)
  • 根据上上条,派生类不应该覆写non-virtual函数
  • 绝不重新定义继承而来的缺省参数值
  • private继承意味着继承实现,是composition,实现的是has-a逻辑。protected继承尽量少用。两种继承在设计层面完全没有意义,只是实现层面的代码重用。
  • 凡是独立的对象都必须有非0大小(空对象会安插一个char)
  • 一个类只负责一件事
  • 一个类只提供一种内聚的接口(不应该让用户依赖于他们不使用的方法)
  • 类内部的类型定义尽量放在public,否则不能作为返回值
  • 类中的大属性都应该用智能指针(或返回STL时应使用move语意)
  • 凡是类内私有变量加m前缀,凡事类似私有仿函数,加or后缀
  • 静态成员初始化函数用静态类替代,可以顺便用个functor



四大函数(构造函数、拷贝构造、赋值、析构)

  • 若有多态继承体系,基类析构函数尽量声明为virtual
  • 如果类内new了对象,并且该类负责delete,则必须要定义拷贝构造函数和赋值操作符。
  • 若不想使用编译器自动生成的函数,就该默认拒绝(将其声明在private或protected)
  • 别让异常逃离析构函数,C++不喜欢析构函数吐出异常
  • 绝不在构造函数或析构函数中调用virtual函数(当然其他函数调用virtual可以实现template method等有趣的模式)
  • 拷贝函数应该确保拷贝了对象内所有成员和基类部分
  • 不要以某个拷贝函数去实现另外一个拷贝函数(一个是copy函数,一个是copy assignment函数),应该把共同部分放在第三个独立函数


函数:

  • 尽量将函数参数声明为const
  • 另operator =返回一个*this的引用,并且在operator =中处理自我赋值
  • 函数参数的构造顺序不确定,所以不要在函数参数中执行new操作,或将多个函数参数都用函数来表示(否则若一个发生异常,其他有可能不会执行)。
  • 尽量用传递const 引用代替传值
  • non-member,none-friend函数有封装性好,跨类型操作等能力,因此在需要的时候没有必要局限于把函数全部放到类里的传统规则。
  • 若所有参数都需要类型转换,请为此采用non-memeber函数(典型的是双目操作符重载)
  • 凡是需要对指针参数做提领操作,都需要检查是否为BULL
  • 发布版程序维持程序的勉强工作比crash更好;debug版让程序尽量crash。(例如对NULL指针的提领)



代码布局:

  • 头文件尽量不要包含头文件,尽可能的用前置声明。
  • 类的声明和实现应该分开(template没有export可以用时得谨慎)
  • 若必须要在同一文件中交叉引用定义,则可考虑类中类
  • 所有类都该有自己的名称空间。


杂项:

  • 尽可能用const enum inline 替换#define
  • 尽可能使用const
  • 确定对象调用前已先被初始化。(包括基本对象、类对象、类成员)
  • 使用引用计数型智慧指针时,应该注意RCSPs无法打破环状引用
  • 成员使用new和delete时应采用相同的形式
  • 尽可能延后变量定义式的出现时间(Singleton)
  • 尽可能少用转型操作,(reinterpret_cast,static_cast,const_cast太危险,dynamic_cast效率太低),但如果要转型,尽量用C++的*cast转型关键字。
  • 输入流的getline尽量用全局getline函数。(可以用string)
  • 不用临时变量,方便调试
  • 轻量级对象应尽量多用临时对象
  • 引用只用于立即处理,若需要长久保存,则得用堆
  • 数值类型转化要用显式,尽量避免用隐式

模版
  • 大项目中,尽量不要使用显式实例化
  • 尽量使用包含模型组织模版代码
  • 模版函数所有重载版本的声明都应位于被调用位置之前
  • 模版函数的重载应只改变参数数或显式指定模版参数
  • 模版参数两个>之间要求有空格
  • 不得不用template不能采用分离模型时,对成员模版函数的特化必须在类外部用inline特化,否则会有重复定义问题。
    模版构造函数不能在类内定义,必须要在类外定义。如果有模版构造函数的需求,尽量全部在类体外const inline定义(支持export的话可以考虑)
资源管理:
  • 一旦使用了资源,必须归还,并且谁用谁归还(包括内存、文件描述器、互斥锁、图形界面中的字型和笔刷、数据库连接、网络socket)
  • 用对象来管理资源
  • 资源取得的时机便是初始化的时机。
  • 在资源管理类中小心coping行为
  • 在资源管理类中提供对原始资源的访问
  • RAII





STL:

  • 可以用for_each来避免显示使用迭代器
  • std内部是深拷贝


设计模式:

  • 全局唯一对象尽量用Singleton,而不是用static
  • 可以用non-virtual Interface的方式实现template method
  • 用tr1::funtion实现strategy


原创粉丝点击