【异常机制】异常抛出变量的生命周期
当我们throw出类对象时,使用catch捕获异常时有三种选择,分别是捕获对象元素、捕获引用和捕获指针,那么这三种情况下,捕获到的变量是如何分配内存,他的生命周期又是如何呢,首先结论如下:
- 捕获类对象的元素:调用拷贝构造函数把抛出的对象元素拷贝给catch的参数对象元素,调用拷贝构造函数;
- 捕获类对象的引用:catch语句中的对象直接使用抛出的对象;
- 捕获类对象的指针:需要手动new和delete控制内存;
结论如上,下面通过一个程序详细探究(提示:因为catch严格按照类型匹配进行接异常,所以catch元素和catch引用不能同时出现)。
#include <iostream>
using namespace std;
class pIsNULL
{
public:
pIsNULL()
{
cout << "pIsNULL 无参构造函数" << endl;
}
//pIsNULL(pIsNULL& p)
//错误 C2440 “throw” : 无法从“pIsNULL”转换为“pIsNULL”
//错误(活动) E0334 类 "pIsNULL" 没有适当的复制构造函数
pIsNULL(const pIsNULL& p) //拷贝构造函数要加 const
{
cout << "pIsNULL 拷贝构造函数" << endl;
}
~pIsNULL()
{
cout << "pIsNULL 析构函数" << endl;
}
public:
void print_err_type()
{
cout << "异常原因:指针指向NULL" << endl;
}
};
void print_str(char* str)
{
if (str == NULL)
{
throw pIsNULL(); //调用无参构造函数
}
cout << str << endl;
}
void TestFunc1()
{
char buf1[] = "hello";
char* buf2 = NULL;
try
{
print_str(buf2);
}
catch (pIsNULL e) //调用拷贝构造函数,将 throw 出的对象复制给 e
{
e.print_err_type();
}
catch (...)
{
cout << "未知异常" << endl;
}
}
void TestFunc2()
{
char buf1[] = "hello";
char* buf2 = NULL;
try
{
print_str(buf2);
}
catch (pIsNULL& e) //不会调用拷贝构造函数
{
e.print_err_type();
}
catch (...)
{
cout << "未知异常" << endl;
}
}
void print_str2(char* str)
{
if (str == NULL)
{
throw new pIsNULL;
}
cout << str << endl;
}
void TestFunc3()
{
char buf1[] = "hello";
char* buf2 = NULL;
try
{
print_str2(buf2);
}
catch (pIsNULL* e)
{
e->print_err_type();
delete e;
}
catch (...)
{
cout << "未知异常" << endl;
}
}
int main()
{
TestFunc1(); //用对象元素接异常
//TestFunc2(); //用引用接异常
//TestFunc3(); //用指针接
system("pause");
return 0;
}
分别在主函数中调用三个测试函数,观察打印结果:
①在主函数中调用第一个测试函数,用元素捕获异常
TestFunc1(); //用对象元素接异常
打印结果如下
可以看到,在catch的时候会将throw处构造的对象通过拷贝构造函数复制给catch语句中的元素e,因为这里一共有两个对象,所以在异常结束时会调用两次析构函数,分别析构两个对象。
②在主函数调用第二个测试函数,用引用捕获异常
TestFunc2(); //用引用接异常
运行结果如下
使用引用捕获异常的时候会直接使用throw处构造的对象,所以不会调用拷贝构造函数,只调用一次析构函数。
③在主函数调用第三个测试函数,用指针捕获异常
TestFunc3(); //用指针接
抛出指针类型的异常最好手动new和delete来管理内存。