//下面的demo代码可以直接拷贝到文件,并且在g++编译器编译后即可执行
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <string>
#include <iostream>
using namespace std;
//定义用于测试的异常类
class MyException
{
public:
MyException (string name="none") : m_name(name)
{
cout << "构造一个MyException异常对象,名称为:"<<m_name<< endl;
}
MyException (const MyException& old_e)
{
m_name = old_e.m_name;
cout << "拷贝一个MyException异常对象,名称为:"<<m_name<< endl;
}
MyException& operator= (const MyException& old_e)
{
m_name = old_e.m_name;
cout << "赋值拷贝一个MyException异常对象,名称为:"<<m_name<< endl;
return *this;
}
virtual ~ MyException ()
{
cout << "销毁一个MyException异常对象,名称为:" <<m_name<< endl;
}
string GetName()
{
return m_name;
}
protected:
string m_name;
};
void test_fun1()
{
try
{
cout << "1" << endl;
// 构造一个异常对象,这是局部变量
MyException ex_obj1("ex_obj1");
cout << "2" << endl;
/* 这里抛出异常对象,注意这时编译器会复制一份新的异常对象-临时变量 - 此时相当于return一份到异常栈中,因此会调用拷贝
构造函数并且在跳转之前,把从try到throw之间的局部变量均析构掉(除了异常栈中的那份继续保留-直到对应的catch block
结束才析构)*/
throw ex_obj1;
cout << "3" << endl;//不会执行到
}
/*这种方式会首先从异常栈中拷贝一份到当前的参数e中,因此调用一次拷贝构造函数,处理完毕后,在catch块结束前
分别调用异常栈中的异常对象和e的析构函数,因此执行两次析构函数的调用,类似函数调用的传参,不同的是异常对象的
实参是从异常栈拷贝到形参e的*/
catch(MyException e)
{
cout << e.GetName() << endl;
//异常栈的异常对象会在catch block 之后调用对应的析构函数销毁掉
}
catch(...)
{
cout<<"catch unknow exception"<<endl; }
}
void test_fun2()
{
try
{
cout << "1" << endl;
// 构造一个异常对象,这是局部变量
MyException ex_obj1("ex_obj1");
cout << "2" << endl;
/*这里抛出异常对象注意这时编译器会复制一份新的异常对象,临时变量 - 此时相当于return一份到异常栈中,因此调用拷贝构造函数
并且在转换之前,把从try到throw直接的局部变量均析构调*/
throw ex_obj1;
}
/*这种方式是直接使用异常栈中的异常对象的引用,因此少了一次拷贝对象到e的拷贝构造函数和catch block后的析构函数的调用*/
catch(MyException &e)
{
cout << e.GetName() << endl;
}
}
//这种方式是最推荐的方法,因为只需构造一次并且析构一次,同时不存在内存泄漏
void test_fun3()
{
try
{
//这种方式是直接构造一个异常对象放到异常栈,然后跳转,因此没有局部临时变量的创建和析构
throw MyException("ex_obj1");
}
//这种方式是直接使用异常栈中的异常对象的引用,因此少了一次拷贝对象到e的拷贝构造函数和catch block后的析构函数的调用
catch(MyException &e)
{
cout << "&e" << endl;
cout << e.GetName() << endl;
}
catch(MyException *e)
{
cout << "*e" << endl;
cout << e->GetName() << endl;
}
}
void test_fun4()
{
try
{
//这种方式是直接构造一个异常对象放到异常栈,然后跳转,因此没有局部临时变量的创建和析构
throw MyException("ex_obj1");
}
//这种方式会首先从异常栈中拷贝一份到当前的参数e中,因此调用一次拷贝构造函数,处理完毕后,在catch块结束前
//分别调用异常栈中的异常对象和e的析构函数,因此执行两次析构函数的调用
catch(MyException e)
{
cout << e.GetName() << endl;
}
}
//下面考虑指针的方式
void test_fun5()
{
try
{
/*可以加入static,让局部变量放到静态存储区,此时可以传地址,并且该对象认为是异常对象,因此在catch block之后也会调用对应的析构函数进行销毁*/
MyException ex_obj1("ex_obj1");
//cout << &ex_obj1 << endl;
//MyException *e = &ex_obj1;
//cout << e->GetName() << endl;
/*这种方式是非法的,此时虽然地址已经抛出去,但对应的exception对象会被析构掉,因此在cache的时候拿到地址也意义不大的
相当于函数调用的return局部变量的地址一样的问题*/
throw &ex_obj1;
}
catch(MyException *e)
{
//cout << e << endl;
cout << e->GetName() << endl;
}
}
void test_fun6()
{
try
{
//不提倡这样的抛出方法,这样如果在catch中不执行delete操作,则会发生内存泄漏
MyException *pEx = new MyException("ex_obj1");
throw pEx;
}
catch(MyException *e)
{
//cout << e << endl;
cout << e->GetName() << endl;
if(NULL != e)
{
delete e; //但无法确认对应的地址是否是new来构建的,有可能是static 的栈分配的,此时虽然是捕捉地址,但无需delete
}
}
}
int main(int iArgC, char ** ppArgV)
{
test_fun1();
test_fun2();
test_fun3();
test_fun4();
test_fun5();
test_fun6();
}