您的位置:首页 > 编程语言 > C语言/C++

More Effective C++ 阅读笔记(八)--“抛出一个异常”与“传递一个参数”或“调用一个虚函数”间的差异

2008-08-06 09:35 337 查看
More Effective C++ 阅读笔记(八)--“抛出一个异常”与“传递一个参数”或“调用一个虚函数”间的差异
黑月亮 发表于 2005-10-3 22:20:00

理解“抛出一个异常”与“传递一个参数”或“调用一个虚函数”间的差异

这之间有三个主要区别。
第一、异常对象在传递时总被进行拷贝;当通过传值方式捕获时,异常对象被拷贝了两次。对象做为参数传递给函数时不

一定需要被拷贝(当用传值的方式传递函数的参数,我们制造了被传递对象的一个拷贝)。
让我们测试一下下面这三种用来捕获Widget异常的catch子句,异常是做为passAndThrowWidgetp抛出的:
catch (Widget w) ... // 通过传值捕获异常
catch (Widget& w) ... // 通过传递引用捕获异常
catch (const Widget& w) ... //通过传递指向const的引用捕获异常
我们立刻注意到了传递参数与传递异常的另一个差异。一个被异常抛出的对象(刚才解释过,总是一个临时对象)可以通过普通的引用捕获;它不需要通过指向const对象的引用(reference-to-const)捕获。在函数调用中不允许转递一个临时对象到一个非const引用类型的参数里(参见条款M19),但是在异常中却被允许。
让我们先不管这个差异,回到异常对象拷贝的测试上来。我们知道当用传值的方式传递函数的参数,我们制造了被传递对象的一个拷贝(参见Effective C++ 条款22),并把这个拷贝存储到函数的参数里。同样我们通过传值的方式传递一个异常时,也是这么做的。当我们这样声明一个catch子句时:
catch (Widget w) ... // 通过传值捕获
会建立两个被抛出对象的拷贝,一个是所有异常都必须建立的临时对象,第二个是把临时对象拷贝进w中(WQ加注,重要:是两个!)。同样,当我们通过引用捕获异常时,
catch (Widget& w) ... // 通过引用捕获
catch (const Widget& w) ... //也通过引用捕获
这仍旧会建立一个被抛出对象的拷贝:拷贝同样是一个临时对象。相反当我们通过引用传递函数参数时,没有进行对象拷贝。当抛出一个异常时,系统构造的(以后会析构掉)被抛出对象的拷贝数比以相同对象做为参数传递给函数时构造的拷贝数要多一个。
我们还没有讨论通过指针抛出异常的情况。不过,通过指针抛出异常与通过指针传递参数是相同的。不论哪种方法都是一个指针的拷贝被传递。但,你不能认为抛出的指针是一个指向局部对象的指针,因为当异常离开局部变量的生存空间时,该局部变量已经被释放。Catch子句将获得一个指向已经不存在的对象的指针。这种行为在设计时应该予以避免。(WQ加注,也就是说:必须是全局的或堆中的。)

第二、对象做为异常被抛出与做为参数传递给函数相比,前者类型转换比后者要少(前者只有两种转换形式)。
在catch子句中进行异常匹配时可以进行两种类型转换。

第一种是继承类与基类间的转换。一个用来捕获基类的catch子句也可以处理派生类类型的异常。例如在标准C++库(STL)定义的异常类层次中的诊断部分(diagnostics portion )(参见Effective C++ 条款49)。
捕获runtime_errors异常的Catch子句可以捕获range_error类型和overflow_error类型的异常;可以接收根类exception异常的catch子句能捕获其任意派生类异常。
这种派生类与基类(inheritance_based)间的异常类型转换可以作用于数值、引用以及指针上:
catch (runtime_error) ... // can catch errors of type
catch (runtime_error&) ... // runtime_error,
catch (const runtime_error&) ... // range_error, or overflow_error
catch (runtime_error*) ... // can catch errors of type
catch (const runtime_error*) ... // runtime_error*,range_error*, or overflow_error*

第二种是允许从一个类型化指针(typed pointer)转变成无类型指针(untyped pointer),所以带有const void* 指针的catch子句能捕获任何类型的指针类型异常:
catch (const void*) ... //捕获任何指针类型异常

最后一点,catch子句进行异常类型匹配的顺序是它们在源代码中出现的顺序,第一个类型匹配成功的catch将被用来执行。当一个对象调用一个虚拟函数时,被选择的函数位于与对象类型匹配最佳的类里,即使该类不是在源代码的最前头。
例如:
try {
...
}
catch (logic_error& ex) { // 这个catch块 将捕获
... // 所有的logic_error异常, 包括它的派生类
}

catch (invalid_argument& ex) { // 这个块永远不会被执行
... //因为所有的invalid_argument异常 都被上面的 catch子句捕获。
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐