于是,遵照大师的教诲,笔者增加了一个辅助类,代码如下:
class C_Handle {
C* _pObj;
public:
C_Handle(C* pObj) {
_pObj = pObj;
_pObj->Editable(true);
// may be other operations
}
~C_Handle(){
_pObj->Editable(false);
// also may be operations according to ctor
}
operator C* () {return _pObj;}
};
C_Handle的构造函数和析构函数中,对_pObj所指对象的操作是成对出现的,所以在以后扩展时也不容易出错。此时f函数的代码也变得简洁了许多:
void f(C* pObj)
{
C_Handle ch(pObj);
try {
// do some work with object
// may cause exception
} catch(…)
{
// do some thing and rethrow
throw;
}
}
个人觉得,这种技法应该具有普遍意义。现总结如下:在某个scope内出现针对某个对象的若干对称操作,而在彼此对称的两组操作间可能抛出异常以破坏这种对称性,并且这种破坏将导致与该scope相关的某种断言为假时,就可以考虑使用类似于Stroustrup博士在处理资源管理问题时所推荐的这种“resource acquisition in initialization”技法。甚至可以认为,资源管理中发生的例子是这里所提到的情形的一个特例。在资源管理方面的另一个很典型的例子是智能指针(Smart Pointer) 。
此外,对于这种方法可能存在的一个缺点是,或许会出现很多类似C_Handle这样的规模很小的辅助类。对此我们可以这样考虑:如果这些类不是很多,那么它们的存在将会给代码的编写和维护带来好处(想想前面提到的维护一致性的代价),并且如果程序中多处出现这样的类似情况时,这些类就可以复用了。而当类的数目多到让你无法容忍时,就该考虑一下其中某些类存在的必要性了,毕竟并非程序的每处都要使用异常,也许你的设计本身存在问题。此外,如果这些辅助类彼此有关联则可以考虑引入继承体系,而如果它们之间的行为及其相似,使用模板机制(template)进行泛化,也不失为一个优化策略。