构造函数,析构函数与异常
构造函数与异常
函数try语句块
函数try语句块使得catch既能处理构造函数体,又能处理构造函数的初始化过程,但是,在初始化构造函数的参数时,也可能发生异常,这样的异常不属于try语句块的一部分,函数try语句只能处理构造函数开始执行后发生的异常。
声明方式:try出现在表示构造函数初始值列表的冒号之前。
#include <iostream>
#include <string>
using namespace std;
class A{
public:
A(string name,int age)try:name(name),age(age){
}catch(...){
cout<<"catch exception!"<<endl;
}
~A(){
cout<<"A::~A()"<<endl;
}
private:
string name;
int age;
};
int main(){
return 0;
}
构造函数中抛出异常
-
构造函数可以抛出异常,但是构造函数如果抛出异常,那么析构函数将不会被调用,可能会造成内存泄露,如下程序中A并没有被析构
-
当构造函数中抛出异常时,对象处于部分构造,已经构造完毕的子对象将会逆序地被析构(即异常点前面的对象),而还没有开始构建的对象将不会被构造了(即异常点后面的对象),因此没有析构过程;正在构建的子对象(基类构造函数)和对象自己本身将会停止构造(即出现异常的对象),并且其析构函数是不会被执行的
#include <iostream>
#include <string>
using namespace std;
class A{
public:
A(){}
~A(){
cout<<"A::~A()"<<endl; //析构函数被调用将会打印A::~A()
}
};
class B{
public:
B(){
a = new A();
throw 1;
}
~B(){
cout<<"B::~B()"<<endl;
}
private:
A *a;
};
int main(){
try{
B b;
}catch(int){
cout<<"catch int exception!"<<endl;
}
return 0;
}
//./a.out
//catch int exception!
//内存泄漏
解决方法:
- 在构造函数先
catch异常
,析构已经成功分配的资源,然后再次throw
- 使用智能指针
auto_ptr
class B{
public:
B(){
a = new A();
try{
throw 1;
}
catch(int){ //抛出异常前会先释放已经成功配的资源
delete a;
throw;
}
}
~B(){
cout<<"B::~B()"<<endl;
}
private:
A *a;
};
//./a.out
//A::~A()
//catch int exception!
总结
- 构造函数可以抛出异常,但会导致析构函数不能被调用,对象本身已经申请到的资源将会被释放(内部成员会逆序调用其析构函数)
- 析构函数未被调用,可能会造成内存泄漏,或系统资源未被释放
- 可以在构造函数内部先释放已分配的资源,然后再抛出异常
析构函数与异常
析构函数中不能抛出异常
- 如果析构函数抛出异常,则异常点之后的程序不会执行,如果析构函数在异常点之后执行了某些必要的动作比如释放某些资源,则这些动作不会执行,会造成诸如资源泄漏的问题。
- 通常异常发生时,c++的机制会调用已经构造对象的析构函数来释放资源,此时若析构函数本身也抛出异常,则前一个异常尚未处理,又有新的异常,会造成程序崩溃的问题。
- 析构函数的异常不能被抛出析构函数之外,需要在析构函数内部进行处理。
#include <iostream>
#include <string>
using namespace std;
class A{
public:
A(string name,int age)try:name(name),age(age){
}catch(...){
cout<<"catch exception!"<<endl;
}
~A(){
try{
throw 1;
}catch(int){
cout<<"catch exception!"<<endl;
}
cout<<"A::~A()"<<endl;
}
private:
string name;
int age;
};
int main(){
A a("zhainankl",22);
return 0;
}