目录
第一章
尽量使用const,enum,inline代替#define
当使用#define NUM 10 的时候, 编译开始的时候NUM可能被预处理器移走,不会进入记号表中,导致之后发生错误的时候只为报错10,而看不见NUM从何而来,如果NUM来自非自己写的头文件,那么更难以追踪10到底是什么
解决方法是用 const int NUM = 10代替
对于常量字符串char来说建议需要用两个const
const char * const STR = “ABC”, 最好替代是用const string STR(“ABC”)
对于类内常量则需要加上static保证只有一份实体Class Car { public: const int WEIGHT = 200; }
这只是声明而不是定义,C++通常要求任何东西都要有定义,所以建议在非头文件下添加 cont int CAR::WEIGHT; 因为声明已经赋过值了 所以这里不用赋值, 当然有些旧编译器不允许static成员在声明的时候赋值,这个时候就在定义的地方赋值。同时如果是#define的话也没办法在public:下使用
当编译器不允许类内初始化static类型的成员的时候,且这个成员用于定义数组的长度时 就应该用枚举
enum { LENGTH=10} int nums[LENGTH];
对于宏函数来说,使用不当会出现意料的错误
#define MAX(a,b) f((a) > (b) ? (a) : (b)) int a = 5, b= 10; MAX(++a,b); //此时a被累加两次
所以建议不要用宏函数 改为模板
总结
1. 单纯常量用const,enum,
2.宏函数用inline来代替
尽量使用const
令函数返回const 对象可以减少程序错误,例如重定义+、-类操作符返回的const对象可以防止出现(a+b) = c 这样计算又被赋值的情况
const成员函数
当对函数进行修饰const的时候,这个重载[]因为const修饰了 所以理论上是不会改变类内元素,但是函数却返回的是char指针,这会带来隐患
class CText { private: char *pChar; public: char* operation[](index) const { return pChar[index]; } } //如果你进行这样的操作 const CText c("hello"); char *ch = c[0]; ch = 'J';//这个时候会改变原来的,变为Jello
你创建一个常量对象,而且只调用他的const函数还是可能改变
当你在const成员函数中想要修改变量的时候,一般来说编译器是不允许的,但你可以通过mutable修饰变量
当const和non-const函数实现的效果是一致的时候,例如有const的[ ]和non-const的[ ], 此时可以用non-const函数调用const函数具体就是利用const_cast添加const 和static_cast去掉const
确定对象被使用前已先被初始化
确保每个变量都被初始化,每个构造函数都将对象的每个成员初始化
类内成员初始化时间早于进入构造函数代码块,所以在代码块中初始化实际上只是被赋值而已, 所以建议使用成员初始化列表进行初始化
建议:总是使用成员初始化列表赋值
成员初始化列表初始数据顺序按照形参顺序
两个不同文件的两个non-local static对象中一方使用另一方的时候可能会因为两个文件编译顺序不同而导致另一方还未初始化就被使用
解决方法是转换为local static (函数内的static) 通过调用函数的方式return静态对象, 当然这在多线程下可能出错,可用单例模式解决
第二章 构造/析构/赋值运算
在你不声明类构造/析构/赋值的时候编译器会帮你自动生成default版本,如果想禁用,那么就放到private作用域内
如果子类继承了这个父类,那么子类也不会有默认函数
别让异常逃离析构函数
析构函数一定不要抛出异常, 可以catch住异常然后处理他(不继续向上抛或用abort函数终止程序运行)
如果客户需要对某个操作函数运行期间抛出异常做出反应,那么应该在class中提供个普通函数(不是直接在析构函数中)执行操作
例如这里提供个close函数给客户使用
绝对不要在构造/析构过程中调用virutal函数,或者是包含的函数中调用virutal函数,父类在构造函数中使用virtual函数不会下降到子类
例如在父类中有个纯虚函数,父类构造函数调用了这个纯虚函数
而子类在初始化的时候是需要先初始化基类部分,调用父类的构造函数的,那么这个时候会发现调用了父类的纯虚函数而不是自己已经重写的
避免的方法就是要么设置为非虚函数要么就是压根不在构造函数中用这个函数
operation=操作符中返回一个本身引用 reference to * this
可以产生这样的效果int a = (b = 3)
在operation=中往往要加一层判断,判断当前元素是不是*this,如果是则直接返回,然而下图也存在异常隐患,如果new Bitmap出现异常的时候那么pb实际上指向的是被删除的区域,
正确做法
先创建副本保存,然后new,之后再删除
这里没有证同测试,不用自我赋值也行得通,自我赋值几率低,会使代码变大,降低效率
复制对象时勿忘其每一个部分
在子类中的编写copy和=操作的时候要注意除了自己成员赋值外,还要将base 类也要进行赋值
不要为了减少代码重复就copy中调用=或 =中调用copy
最好的方法是第三个函数来处理重复部分
第三章 资源管理
以对象管理资源
在函数中通过调用另一个函数(函数内new了内存)返回了指针
那么这个时候这块内存极容易泄漏,最好用shared_ptr只能指针来接收
这样的话智能指针在构造函数中获取资源,在析构函数中释放资源
在管理资源的类中小心copy行为
当对管理资源的类copy的时候,需要格外小心
1.可以禁止复制,复制函数设为private
2.对底层资源进行shared_ptr修饰,并指定删除器,在过期的时候调用某个函数
3.深度拷贝底层资源
4.转移所有权,使用auto_ptr
成对使用new和delete时要采取相同形式
构造函数中有new则析构要有delete
构造有new[ ] 则析构要有delete [ ]
不可混搭, 对new使用delete [] 会出现意外
多个构造的时候只能选择new或者new[ ]一种,否则无法析构
对数组有typedef的时候要格外小心,建议不对数组使用typedef,容易混淆是否是delete [ ]还是delete