More Effective C++ 读书笔记五——异常

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

第一,exception object总是会被复制,如果以by value方式捕捉,它们甚至被复制两次。至于传递给函数参数的对象不一定得复制。第二,“被抛出成为exceptions”的对象,其被允许的类型转换动作,比“被传递到函数去”的对象少。第三,catch子句以其“出现于源代码的顺序”被编译器检查比对,其中第一个匹配成功者便被执行;而当我们以某个对象调用一个虚函数,被选中执行的是那个“与对象类型最佳吻合”的函数。

条款13:以by reference方式捕捉exceptions

上面两个条款都提到了被抛出对象的复制问题,通过下面的几个例子试了下:
[cce lang="cpp"]
#include <iostream>

static int count = 0;

class e_class {
public:
e_class() {
std::cout << "in constructor: count: " << ++count << std::endl;
}
e_class(const e_class &right) {
std::cout << "in copy constructor: count: " << ++count << std::endl;
}
};

int main(){
try {
e_class e_c; //创建对象,调用构造函数count累加
throw e_c; //抛出异常,e_c被复制,调用拷贝构造函数,count累加
} catch (e_class e) { //以by value形式catch,对象再次被复制,count累加
}

return 0;
}
[/cce]
这里count被加了3次,第一次是e_class对象构造的时候,然后抛出去的时候,对象被复制,然后catch的时候因为按值传递,传入对象再次被复制。执行结果为:
in constructor: count: 1
in copy constructor: count: 2
in copy constructor: count: 3
这里能被避免的就是catch这里,如果按照引用传递,这次复制就可以避免:
[cce lang="cpp"]
#include <iostream>

static int count = 0;

class e_class {
public:
e_class() {
std::cout << "in constructor: count: " << ++count << std::endl;
}
e_class(const e_class &right) {
std::cout << "in copy constructor: count: " << ++count << std::endl;
}
};

int main(){
try {
e_class e_c; //创建对象,调用构造函数count累加
throw e_c; //抛出异常,e_c被复制,调用拷贝构造函数,count累加
} catch (e_class &e) { //以by reference形式catch,不会再次被复制
}

return 0;
}
[/cce]
采用按引用方式catch后,和函数按引用传递参数一样,对象不再被复制,执行结果为:
in constructor: count: 1
in copy constructor: count: 2

条款12中还介绍了再次抛出异常的对象复制问题:
[cce lang="cpp"]
#include <iostream>

static int count = 0;

class e_class {
public:
e_class() {
std::cout << "in constructor: count: " << ++count << std::endl;
}
e_class(const e_class &right) {
std::cout << "in copy constructor: count: " << ++count << std::endl;
}
};

int main(){
try {
try {
e_class e_c;
throw e_c;
} catch (e_class &e) {
throw e; //这里抛出的是e的副本,会对e进行复制后抛出
}
}catch(...) {
}

return 0;
}
[/cce]
这里在catch子句中重新抛出了异常,但是这种方式抛出的其实是异常对象的副本,对象会再次被复制,执行结果为:
in constructor: count: 1
in copy constructor: count: 2
in copy constructor: count: 3
如果改成这样:
[cce lang="cpp"]
#include <iostream>

static int count = 0;

class e_class {
public:
e_class() {
std::cout << "in constructor: count: " << ++count << std::endl;
}
e_class(const e_class &right) {
std::cout << "in copy constructor: count: " << ++count << std::endl;
}
};

int main(){
try {
try {
e_class e_c;
throw e_c;
} catch (e_class &e) {
throw; //直接抛出e,不会进行复制
}
}catch(...) {
}

return 0;
}
[/cce]
这里throw语句不带任何参数,抛出的是当前异常本身,当前的异常对象不会再次被复制,执行结果为:
in constructor: count: 1
in copy constructor: count: 2

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据