现在来考虑这样一个构造函数:
Type() : m_a(new TypeA), m_b(new TypeB){}
假设成员变量m_a和m_b是原始的指针类型,并且和Type内的申明顺序一致。这样的代码是不安全的,它存在资源泄漏问题,构造函数的失败回滚机制无法应对这样的问题。如果new TypeB抛出异常,new TypeA返回的资源是得不到释放机会的.曾经,很多人用这样的方法避免异常:
Type() : m_a(NULL), m_b(NULL){
auto_ptr tmp_a(new TypeA);
auto_ptr tmp_b(new TypeB);
m_a = tmp_a.release();
m_b = tmp_b.release();
}
当然,这样的方法确实是能够实现异常安全的代码的,而且其中实现思想将是非常重要的,在如何实现强保证的异常安全代码中会采用这种思想.然而这种做法不够彻底,至少析构函数还是要手动完成的。我们仍然可以借助RAII技术,把这件事做得更为彻底:shared_ptr m_a; shared_ptr m_b;这样,我们就可以轻而易举地写出异常安全的代码:
Type() : m_a(new TypeA), m_b(new TypeB){}
如果你觉得shared_ptr的性能不能满足要求,可以编写一个接口类似scoped_ptr的智能指针类,在析构函数中释放资源即可。如果类设计成不可复制的,也可以直接用scoped_ptr。强烈建议不要把auto_ptr作为数据成员使用,scoped_ptr虽然名字不大好,但是至少很安全而且不会导致混乱。
RAII技术并不仅仅用于上述例子中,所有必须成对出现的操作都可以通过这一技术完成而不必try...catch.下面的代码也是常见的:
a_lock.lock();
try{ ...} catch(...) {a_lock.unlock();throw;}
a_lock.unlock();
可以这样解决,先提供一个成对操作的辅助类:
struct scoped_lock{
explicit scoped_lock(Lock