异常都是人造的?
程序能够在存在异常的情况下正常运行是因为它们按照要求进行了设计,而
不是因为巧合。异常安全(Exception-safe)的程序不是偶然建立的。一个没有按照要求进
行设计的程序在存在异常的情况下运行正常的概率与一个没有按照多线程要求进行设计的
程序在多线程的环境下运行正常的概率相同,概率为 0。
异常处理作用
提高自己软件的正确性,强壮性和高效性。
Item M9:使用析构函数防止资源泄漏
auto_ptr可以有效解决构造函数内存泄漏问题。举例:
代码段1:
ALA *pa = readALA(dataSource); //得到下一个动物
pa->processAdoption(); //处理收容动物
delete pa; //删除 readALA 返回的对象
这个函数循环遍历 dataSource 内的信息,处理它所遇到的每个项目。唯一要记住的一
点是在每次循环结尾处删除 pa。这是必须的,因为每次调用 readALA 都建立一个堆对象。
如果不删除对象,循环将产生资源泄漏。
现在考虑一下,如果 pa->processAdoption 抛出了一个异常,将会发生什么?
processAdoptions 没有捕获异常,所以异常将传递给 processAdoptions 的调用者。传递中,processAdoptions 函数中的调用 pa->processAdoption 语句后的所有语句都被跳过,这就是说 pa 没有被删除。结果,任何时候 pa->processAdoption 抛出一个异常都会导致
processAdoptions 内存泄漏。
优化后代码段2:
ALA *pa = readALA(dataSource);
try {
pa->processAdoption();
}
catch (...) { // 捕获所有异常
delete pa; // 避免内存泄漏
// 当异常抛出时
throw; // 传送异常给调用者
}
delete pa; // 避免资源泄漏
} // 当没有异常抛出时
但是你必须用 try和 catch对你的代码进行小改动。更重要的是你必须写双份清除代码,
一个为正常的运行准备,一个为异常发生时准备。在这种情况下,必须写两个 delete 代码。
象其它重复代码一样,这种代码写起来令人心烦又难于维护,而且它看上去好像存在着问题。
不论我们是让 processAdoptions 正常返回还是抛出异常,我们都需要删除 pa,所以为什么
我们必须要在多个地方编写删除代码呢?
我们可以把总被执行的清除代码放入 processAdoptions 函数内的局部对象的析构函数
里,这样可以避免重复书写清除代码。因为当函数返回时局部对象总是被释放,无论函数是
如何退出的。这时候我们就可以使用auto_ptr。
优化后代码段3:
while (dataSource) {
auto_ptr<ALA> pa(readALA(dataSource));
pa->processAdoption();
auto_ptr类:
template<class T>
class auto_ptr {
public:
auto_ptr(T *p = 0): ptr(p) {} // 保存 ptr,指向对象
~auto_ptr() { delete ptr; } // 删除 ptr 指向的对象
private:
T *ptr; // raw ptr to object
};
auto_ptr 的思想:用一个对象存储需要被自动释放的资源,然后依靠对象
的析构函数来释放资源。
auto_ptr代价:不要不加思考地把指针替换为auto_ptr来防止内存泄漏,auto_ptr并不是万能的,使用它们是需要一定的开销的;
通常在项目开发过程中,如果有很多地方都要使用某个对象指针,如果用普通指针可能会出现忘记回收导致内存泄漏的情况,或者删除指针对象时机太早,导致空悬指针问题,这时候可以尝试使用智能指针来解决问题。