《More Effective C++》读书笔记-异常

9、利用destructors 避免泄漏资源

使用指针时,如果在delete指针之前产生异常,将会导致不能删除指针,从而产生资源泄漏。解决办法:
1、使用try-catch语句。

int *pi = new int[10000];
std::cout<<pi<<std::endl;
try
{
    func();//若此处异常而未处理,无法执行delete语句,造成内存泄漏
}
catch(std::runtime_error& error)//捕捉所有exceptions
{
    delete pi;//当抛出exception时,执行该语句,避免泄露
    throw;//将exception传给调用端
}
delete pi;//若没有抛出exception,也要避免资源泄露

2、最好的方法:智能指针。使用对象封装资源,如使用auto_ptr,使得资源能够自动被释放。

int *pi = new int[10000];
std::auto_ptr<int> ap(pi);//用 auto_ptr包装一下
std::cout<<pi<<std::endl;
func();

10、在 constructors 内阻止资源泄漏

在构造函数中防止资源泄漏。

类中存在指针时,如果构造函数中出现异常,C++拒绝为没有完成构造函数的对象调用析构函数,原因是避免开销,那么异常将导致以前初始化的其它指针成员不能删除,从而产生资源泄漏。

解决办法是在构造函数中考虑异常处理,产生异常时释放已分配的资源,最好的方法是使用对象封装资源。一般建议不要在构造函数里做过多的资源分配。

11、禁止异常流出 destructors 之外

禁止异常传递到析构函数外的两个原因:
1、能够在异常传递的堆栈辗转开解(stack-unwinding)的过程中,防止程序调用terminate终止。
2、它能帮助确保析构函数总能完成我们希望它做的所有事情。而如果发生异常,则异常后面的代码将不执行,无法确保完成我们想做的清理工作。

解决方法:如果异常不可避免,则应在析构函数内捕获,而不应当抛出。在析构函数中使用try-catch块屏蔽所有异常。

12、了解”抛出一个 exception”与”传递一个参数”或”调用一个虚函数”之间的差异

有三个主要区别:

1、异常对象在传递时总是被复制。当通过传值方式捕获时,异常对象被复制两次。对象作为参数传递给函数时不一定需要被复制。

2、对象作为异常被抛出与作为参数传递给函数相比,前者类型转换比后者少(前者只有两种转换形式:继承类与基类的转换,类型化指针到无类型指针的转换)。

3、catch子句进行异常类型匹配的顺序是它们在源代码中出现的顺序,第一个类型匹配成功的被执行;当一个对象调用一个虚函数时,被选择的函数位于与对象类型匹配最佳的类里,即使该类不是在源代码的最前头。

13、以 by reference 方式捕捉 exceptions

捕获异常的三种方式:

1、指针。用指针方式来捕捉异常,效率最高,在搬移异常信息的时候不需复制对象。但是这种方式只能运用于全局或静态的对象,否则的话由于局部对象离开作用域被销毁,catch中的指针指向不复存在的对象。

若采用抛出一个指针的做法,指向一个新的堆对象,建立在堆中的对象必须删除,而对于不是建立在堆中的对象,删除它会造成不可预测的后果,因此将面临一个难题:对象建立在堆中还是不在堆中?到底是该删除还是不删除?

2、传值。可以解决指针中是否需要删除的问题。但会有新的问题:
(1)异常对象被抛出时系统将对异常对象拷贝两次;
(2)而且它会产生对象切割,即派生类的异常对象被作为基类异常对象捕获时,它的派生类行为就被切割掉了,这样产生的对象实际上是基类对象。

3、引用。完美解决以上问题。不像指针那样,它不会发生对象删除问题,不难捕捉标准的exception。和传值也不一样,没有切割问题,异常对象只会复制一次。

14、明智运用异常规范(exception specifications)

如果一个异常未在已列出的异常规格当中,特殊函数unexpected会被调用。而unexpected的默认行为是调用terminate,而terminate的默认行为是调用abort,所以程序如果违反exception specification,默认结果是终止程序。

避免调用unexpected函数的三种办法:
1、避免在带有类型参数的模板内使用异常规格。因为我们没有办法知道某种模板类型参数抛出什么样的异常,所以不可能为一个模板提供一个有意义的异常规格。

2、如果在一个函数内调用其它没有异常规格的函数时应该去除这个函数的异常规格。

3、处理系统可能抛出的异常。可以将所有的 unexpected异常都被替换为自定义的异常对象,或者替换unexpected函数,使其重新抛出当前异常,这样异常将被替换为 bad_exception,从而代替原来的异常继续传递。

15、了解异常处理的成本

略过。

你可能感兴趣的:(异常)