http://www.programlife.net/throw-exception-in-constructor.html
很重要!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
在构造函数抛出异常后析构函数将不再被调用
我发现要从构造函数中将错误信息传递给类的使用者有一个很简便的方法,那就是抛出异常,这样就可以直接传递字符串之类的信息了。如果是从一个普通的函数传回错误信息的话,以前往往选择返回一个错误代码,然后定义一个字符串二维数组,用相应的错误代号来取得错误信息。
但是如果构造函数要是抛出了异常,这个类的析构函数将不会被调用。如果不知道这一点,那么在构造函数中抛出异常就很危险了。析构函数不被调用,意味着清理工作不会被执行,会导致内存泄露、资源泄露等,这会给我们的产品带来很差的用户体验。
还是先来测试一下吧,证明这个结论是正确的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| // Coded by 代码疯子
// Blog: http://www.programlife.net/
// 构造函数抛出异常测试代码
#include <iostream>
#include <stdexcept>
#include <string>
using namespace std;
class MyExcept
{
public:
explicit MyExcept(string err) : errmsg(err) {}
string getErrMsg() { return errmsg; }
private:
string errmsg;
};
class Demo
{
public:
Demo () { throw MyExcept("Throw exception in constructor"); }
~Demo() { cout << "Will destructor be called? "; }
};
int main(int argc, char **argv)
{
try
{
Demo d;
}
catch (MyExcept& e)
{
cout << e.getErrMsg() << endl;
}
return 0;
} |
执行这段代码所生成的可执行文件,发现析构函数是不会被调用的。那么如果在普通成员函数抛出异常呢?析构函数会不会执行呢?(这里所讨论的情况是保证构造函数不会抛出异常,即在构造函数中不去调用可能抛出异常的函数(即便调用了也不抛出异常的情况))。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
| // Coded by 代码疯子
// Blog: http://www.programlife.net/
// 成员函数抛出异常测试代码
#include <iostream>
#include <stdexcept>
#include <string>
using namespace std;
class MyExcept
{
public:
explicit MyExcept(string err) : errmsg(err) {}
string getErrMsg() { return errmsg; }
private:
string errmsg;
};
class Demo
{
public:
void except_test()
{
throw MyExcept("Throw exception in member function");
}
Demo () {}
~Demo()
{
cout << "Will destructor be called? " << endl;
}
};
int main(int argc, char **argv)
{
try
{
Demo d;
d.except_test();
}
catch (MyExcept& e)
{
cout << e.getErrMsg() << endl;
}
return 0;
} |
![在C++成员函数中抛出异常,析构函数被调用 在C++成员函数中抛出异常,析构函数被调用](http://plcdn.qiniudn.com/wp-content/uploads/2011/05/throw-exception-in-member-function.jpg)
可以看到析构函数在离开try块的时候调用了,然后进入catch块输出异常信息。
那如果一定要在构造函数中抛出异常怎么办?我觉得,可以把清理工作放到一个单独的函数中,在构造函数中抛出异常之前先调用这个函数进行清理工作。当然,在析构函数中也要调用这个函数(这样正常执行析构函数时也可以清理了)。