C++异常处理

C++异常处理

1.C++异常抛出与捕获

#include <iostream>
using namespace std;
//自定义异常类,其实跟普通类没什么区别,任何类型都可以作为异常被抛出
class MyError 
{
	const char * const m_pData;
public:
	MyError(const char * const p=NULL):m_pData(p){}
	const char * what() 
	{
		return m_pData;
	}
};
class MyErrorChild1:public MyError
{
public:
	MyErrorChild1(const char * const p=NULL):MyError(p){}
};
class MyErrorChild2:public MyError
{
public:
	MyErrorChild2(const char * const p=NULL):MyError(p){}
};
//函数头中throw是异常规格说明,用于告诉函数调用者该函数可能抛出的异常类型,去掉不影响执行结果
void func(int i) throw(int,MyError,MyErrorChild1,MyErrorChild2,char) 
{
	cout<<"begin func "<<i<<endl;
	switch(i)
	{
	case 1:throw int(1);break; //抛出一个int类型异常,任何类型都可以作为异常抛出
	case 2:throw MyError("MyError");break;
	case 3:throw MyErrorChild1("MyErrorChild1");break;
	case 4:throw MyErrorChild2("MyErrorChild2");break;
	case 5:throw 'a';break; 
	default:cout<<"no throw"<<endl;
	}
	//异常在该函数中没有被捕获,会被抛到上层函数,下面语句会被跳过
	cout<<"end func"<<endl;
}

int main()
{
	while(1)
	{
		try
		{	
			cout<<"please input a int(0 exit):";
			int i;
			cin>>i;
			if (i==0) break;
			func(i);
		}
		catch (int i) //通过catch捕获异常
		{
			cout<<"int catch "<<i<<endl;
		}
		//异常为对象时最好用引用,防止对象拷贝和对象切割
		catch (MyErrorChild1 &e) //多条catch捕获不同类型的异常,异常被捕获后后续catch不再执行
		{
			cout<<"MyErrorChild1 catch "<<e.what()<<endl;
		}
		catch (MyError &e) //可以捕获MyError类型及其子类型的异常
		{
			cout<<"MyError catch "<<e.what()<<endl;
		}
		catch (...) //捕获剩余所有类型的异常
		{
			cout<<"catch all leave"<<endl;
                        //throw; //此处还可以再通过throw将异常抛出
		}
		//异常被捕获后,继续执行后面的语句,所以下面语句会被执行
		cout<<"while end"<<endl;
	}
	cout<<"main exit"<<endl;
	return 0;
}
执行结果:

please input a int(0 exit):1
begin func 1
int catch 1
while end
please input a int(0 exit):2
begin func 2
MyError catch MyError
while end
please input a int(0 exit):3
begin func 3
MyErrorChild1 catch MyErrorChild1
while end
please input a int(0 exit):4
begin func 4
MyError catch MyErrorChild2
while end
please input a int(0 exit):5
begin func 5
catch all leave
while end
please input a int(0 exit):0
main exit

2.标准异常--C++标准库定义的异常



#include <iostream>
using namespace std;
void func1()
{
	throw bad_cast("bad_cast");		
}
void func2()
{
	throw bad_alloc("bad_alloc");
}
void func3()
{
	throw overflow_error("overflow_error");
}
void func4()
{
	throw underflow_error("underflow_error");
}
void func5()
{
	throw range_error("range_error");
}
void func6()
{
	throw runtime_error("runtime_error");
}
void func7()
{
	throw domain_error("domain_error");
}
void func8()
{
	throw invalid_argument("invalid_argument");
}
void func9()
{
	throw out_of_range("out_of_range");
}
void func10()
{
	throw length_error("length_error");
}
void func11()
{
	throw logic_error("logic_error");
}
void func(int i)
{
	cout<<"begin func "<<i<<endl;
	switch(i)
	{
	case 1:func1();break; 
	case 2:func2();break; 
	case 3:func3();break; 
	case 4:func4();break; 
	case 5:func5();break; 
	case 6:func6();break; 
	case 7:func7();break; 
	case 8:func8();break; 
	case 9:func9();break; 
	case 10:func10();break; 
	case 11:func11();break; 
	default:cout<<"no throw"<<endl;
	}
}

int main()
{
	while(1)
	{
		try
		{	
			cout<<"please input a int(0 exit):";
			int i;
			cin>>i;
			if (i==0) break;
			func(i);
		}
		catch (bad_cast &e) 
		{
			cout<<"catch bad_cast "<<e.what()<<endl;
		}
		catch (bad_alloc &e) 
		{
			cout<<"catch bad_alloc "<<e.what()<<endl;
		}
		catch (overflow_error &e) 
		{
			cout<<"catch overflow_error "<<e.what()<<endl;
		}
		catch (underflow_error &e) 
		{
			cout<<"catch underflow_error "<<e.what()<<endl;
		}
		catch (range_error &e) 
		{
			cout<<"catch range_error "<<e.what()<<endl;
		}
		catch (runtime_error &e) 
		{
			cout<<"catch runtime_error "<<e.what()<<endl;
		}
		catch (domain_error &e) 
		{
			cout<<"catch domain_error "<<e.what()<<endl;
		}
		catch (invalid_argument &e) 
		{
			cout<<"catch invalid_argument "<<e.what()<<endl;
		}
		catch (out_of_range &e) 
		{
			cout<<"catch out_of_range "<<e.what()<<endl;
		}
		catch (length_error &e) 
		{
			cout<<"catch length_error "<<e.what()<<endl;
		}
		catch (logic_error &e) 
		{
			cout<<"catch logic_error "<<e.what()<<endl;
		}
		catch (exception &e)
		{
			cout<<"catch exception "<<e.what()<<endl;
		}
	}
	cout<<"main exit"<<endl;
	return 0;
}


3. set_terminate() 和 set_unexpected()。

(1)set_terminate(),程序中任何未被catch捕获的异常都会触发库函数terminate(),一旦terminate()被调用代表程序已经无法挽回。 库函数terminate()默认操作是调用abort异常退出程序。  可以通过set_terminate设置自定义的terminate()函数。以下情况会触发terminate()函数:

         1》catch列表未捕获到异常;

         2》局部对象的析构函数调用时抛出异常可以被上层函数捕获,但如果异常栈反解过程中抛出异常将无法被捕获;

         3》全局对象或静态对象构造函数或析构函数调用时抛出异常;

         4》库函数unexpected()被调用时会触发terminate()的调用。

(2)set_unexpected(),抛出未在异常规格说明中说明的异常时会触发库函数unexpected(),可以通过set_unexpected设置自定义unexpected函数(vc6.0不支持set_unexpected)。unexpected()中可以通过throw再将异常抛回到原点。

#include <iostream>
using namespace std;
class MyError1{};
class MyError2{};
class MyError3{};
/*
函数头中throw是异常规格说明,用于告诉函数调用者该函数只可能抛出的异常类型,throw(MyError1,MyError2) 告诉编译器func只可能抛出MyError1、MyError2两种类型的异常,如果抛出MyError3则会触发unexpected()函数。
void func(int i);告诉编译器func不确定会抛出什么类型的异常,即可能抛出任何类型的异常。
void func(int i) throw();告诉编译器func不会抛出任何类型的异常。
*/
void func(int i) throw(MyError1,MyError2) 
{
	cout<<"begin func "<<i<<endl;
	switch(i)
	{
	case 1:throw MyError1();break; //MyError1会被捕获
	case 2:throw MyError2();break; //MyError2不会被捕获,会触发terminate
	case 3:throw MyError3();break; //MyError3不包含在异常规格列表中,会触发unexpected,my_unexpected又把异常MyError1抛出到这里,又被捕获到,程序继续运行
	default:cout<<"no throw"<<endl;
	}
}
//用户自定义unexpected()函数
void my_unexpected()
{
	cout<<"unexpected and throw MyError1"<<endl;
	throw MyError1(); //可以通过throw将异常MyError1抛回到原点,这样可能让程序继续运行;也可以调用exit(1)退出程序。
}
//用户自定义terminate()函数
void my_terminate()
{
	cout<<"terminate and exit"<<endl;
	exit(1); //terminate中唯一能做的就是退出程序。
}
int main()
{
	void (*p)() = set_unexpected(my_unexpected);//设置自定义unexpected()
	void (*q)() = set_terminate(my_terminate);//设置自定义terminate()
	while(1)
	{
		try
		{	
			cout<<"please input a int(0 exit):";
			int i;
			cin>>i;
			if (i==0) break;
			func(i);
		}
		catch (MyError1 &) 
		{
			cout<<"catch MyError1"<<endl;
		}
	}
	cout<<"main exit"<<endl;
	return 0;
}

执行结果:

please input a int(0 exit):1
begin func 1
catch MyError1
please input a int(0 exit):3
begin func 3
unexpected and throw MyError1
catch MyError1
please input a int(0 exit):2
begin func 2
terminate and exit


4.栈反解技术

       C++异常处理必须保证程序执行流程离开一个作用域的时候,对于属于这个作用域的所有由构造函数创建的对象,它们的析构函数一定会被调用。这种技术称为栈反解技术,由C++编译器实现。该技术作用于:

       1》函数调用时,在栈中申请的局部对象(不能是对象指针,new创建的对象在堆上),如果构造函数调用成功后抛出异常,离开当前函数作用域时其析构函数会被调用。

       2》构造函数调用过程中如果抛出异常,对象的成员对象(不能是对象指针)如果已经调用自己构造函数初始化成功的,离开当前构造函数作用域时其析构函数会被调用。

       3》析构函数调用过程中如果抛出异常,对象所有成员对象都会调用其析构函数。(析构函数抛出异常程序往往认为是垃圾的程序,是不能容忍的,栈反解过程中调用析构函数如果抛出异常是无法捕获的,只能触发terminate()导致程序终止)

       栈反解技术作用:所有的对象资源都变成原子资源,哪怕在异常情况下,只要对象资源被成功创建,就肯定会被释放。

#include <iostream>       
using namespace std;
class CA
{
private:
	static int count;
public:
	CA()
	{
		if(count==2) throw 2;
		cout<<"CA"<<++count<<endl;
	}
	~CA()
	{
		cout<<"~CA"<<count--<<endl;
	}
};
int CA::count =0;
class CB
{
	CA b[4];
};
class CC
{
	CA b[2];
public:
	~CC()
	{
		throw 2;
	}
}; 
void func(int i)
{
	if(i==1) 
	{
		CA b[4]; //b[2]创建时抛出异常,b[0]、b[1]被正常释放 	
	}
	else if(i==2)
	{
		CB b;   //b.b[2]创建时抛出异常,b.b[0]、b.b[1]被正常释放 
	}
	else if(i==3)
	{
		CC c;  //c析构函数抛出异常,c.b被正常释放
	}
}
int main () 
{	
	while(1)
	{
		try
		{
			cout<<"please input a int(0 exit):";  
            int i;  
            cin>>i;  
            if (i==0) break;  
            func(i);  
		}
		catch (...)
		{
			cout<<"catch exception"<<endl;
		}
	}
	return 0;
}
       以上实例程序验证了栈反解的功能,但要利用栈反解过程必须定义对象的实例(不能定义成指针形式),C++一般的解决方案是 RAIIResource Acquisition Is Initialization),C++内置的RAII即为智能指针auto_ptr。

//vc6.0C++库智能指针实现
template<class _Ty>
class auto_ptr 
{
public:
	typedef _Ty element_type;
	explicit auto_ptr(_Ty *_P = 0):_Owns(_P != 0), _Ptr(_P) {}
	auto_ptr(const auto_ptr<_Ty>& _Y):_Owns(_Y._Owns), _Ptr(_Y.release()) {}
	auto_ptr<_Ty>& operator=(const auto_ptr<_Ty>& _Y)
	{
		if (this != &_Y)
		{
			if (_Ptr != _Y.get())
			{
				if (_Owns)
				delete _Ptr;
				_Owns = _Y._Owns; 
			}
			else if (_Y._Owns) _Owns = true;
			_Ptr = _Y.release();
		}
		return (*this);
	}
	~auto_ptr()
	{
		if (_Owns) delete _Ptr; 
	}
	_Ty& operator*() const
	{
		return (*get()); 
	}
	_Ty *operator->() 
	{
		return (get()); 
	}
	_Ty *get() const 
	{
		return (_Ptr); 
	}
	_Ty *release() const 
	{
		((auto_ptr<_Ty> *)this)->_Owns = false;
		return (_Ptr); 
	}
private:
	bool _Owns;
	_Ty *_Ptr;
};









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值