Item 39. Exception Safe Functions 编写异常安全代码的难点不在于抛出和捕获异常,而是在于抛出和捕获之间要做的事情。当异常从抛出 到达捕获语句的过程中,这期间执行的函数在弹栈前需要清理它所使用的任何资源。通常,这就需要片 刻的思考以及一些常识。 以String的赋值操作为例: String &String::operator =( const char *str ) { if( !str ) str = ""; char *tmp = strcpy( new char[ strlen(str)+1 ], str ); delete [] s_; s_ = tmp; return *this; } char *tmp 这个中间变量似乎有点多余,我们“可以”写成这样: String &String::operator =( const char *str ) { delete [] s_; if( !str ) str = ""; s_ = strcpy( new char[ strlen(str)+1 ], str ); return *this; } 果真如此吗? delete [] 根据约定可以保证不抛出异常,然而new[]可能抛出异常。在未知新的内存是否分配成功的 时候,我们却把s_的内存释放掉了。于是,String对象处于一个bad state。 Herb Sutter 告诉我们在这种情况下应该这样处理:首先在不改变重要状态的一边处理那些能够引发异 常的操作,而后用不能引发异常的操作结束整个过程(First do anything that could cause an exception "off to the side" without changing important state, and then use operations that can't throw an exception to finish up.)。 再看一例: 错误的写法: void Button::setAction( const Action *newAction ) { delete action_; // change state! action_ = newAction->clone(); // then maybe throw? } 繁琐的写法: void Button::setAction( const Action *newAction ) { delete action_; try { action_ = newAction->clone(); } catch( ... ) { action_ = 0; throw; } } 简单正确的写法: void Button::setAction( const Action *newAction ) { Action *temp = newAction->clone(); // off to the side... delete action_; // then change state! action_ = temp; } 
|