中国大学MOOC程序设计与算法(三):C++ 面向对象程序设计 第十周 C++11新特性和C++高级主题 笔记 之 异常处理

第十周 C++11新特性和C++高级主题
1.C++11新特性(一)
2.C++11新特性(二)
3.强制类型转换
4.异常处理

4.异常处理,课程最后一节

程序运行发生异常

程序运行中总难免发生错误
(1)数组元素的下标超界、访问NULL指针
(2)除数为0
(3)动态内存分配new需要的存储空间太大
……
引起这些异常情况的原因可能是:
(1)代码质量不高,存在BUG
(2)输入数据不符合要求
(3)程序的算法设计时考虑不周到
…….
发生异常怎么办
(1)不只是简单地终止程序运行
(2)能够反馈异常情况的信息:哪一段代码发生的、什么异常
(3)能够对程序运行中已发生的事情做些处理:取消对输入文件的改动、释放已经申请的系统资源
……

通常的做法是:在预计会发生异常的地方,加入相应的代码,但这种做法并不总是适用的
……//对文件A进行了相关的操作。
fun(arg, ……);//可能发生异常
……
调用者该如何知道fun(arg, …)是否发生异常?没有发生异常,可以继续执行;发生异常,应该在结束程序运行前还原对文件A的操作。但是,
fun(arg, …)是别人已经开发好的代码
fun(arg, …)的编写者不知道其他人会如何使用这个函数
fun(arg, …)会出现在表达式中,通过返回值的方式区分是否发生异常,不符合编写程序的习惯,而且可能发生多种异常,通过返回值判断也很麻烦。
那么,需要一种手段:(1)把异常与函数的接口分开,并且能够区分不同的异常;(2)在函数体外捕获所发生的异常,并提供更多的异常信息.

异常处理

一个函数运行期间可能产生异常。在函数内部对异常进行处理未必合适。因为函数设计者无法知道函数调用者希望如何处理异常。
告知函数调用者发生了异常,让函数调用者处理比较好
用函数返回值告知异常不方便

用try、catch进行异常处理

try块包含有可能产生异常的语句,如果遇到异常就用throw抛出异常,用类型表示异常类型。一旦遇到异常,throw抛出后就跳出try块,进入catch块。一个try块后可以跟很多个catch块,但是catch块只有一个会被真的执行,因为遇到异常就结束try块了,异常只有一个。如果try块没有抛出异常,就不会进入任何catch块。catch块捕获任何类型的异常,参数匹配try块中throw出的数据类型,catch(…)捕获任何类型的异常,可以作为最后一个catch块,防止有异常类型没有被捕获。

#include 
using namespace std;
int main()
{
	double m ,n;
	cin >> m >> n;
	try {
		cout << "before dividing." << endl;
		if( n == 0)
			throw -1; //抛出int类型异常
		else
			cout << m / n << endl;
		cout << "after dividing." << endl;
	}
	catch(double d) {
		cout << "catch(double) " << d << endl;
	}
	catch(int e) {
		cout << "catch(int) " << e << endl;
	}
	cout << "finished" << endl;
	return 0;
}
程序运行结果如下:
9 6↙
before dividing.
1.5
after dividing.
finished
#include 
using namespace std;
int main()
{
	double m ,n;
	cin >> m >> n;
	try {
		cout << "before dividing." << endl;
		if( n == 0)
			throw -1; //抛出整型异常
		else if( m == 0 )
			throw -1.0; //抛出double型异常
		else
			cout << m / n << endl;
		cout << "after dividing." << endl;
	}
	catch(double d) {
		cout << "catch(double) " << d << endl;
	}
	catch(...) {
		cout << "catch(...) " << endl;
	}
	cout << "finished" << endl;
	return 0;
}
程序运行结果:
9 0↙
before dividing.
catch(...)
finished

0 6↙
before dividing.
catch(double) -1
finished

异常的再抛出

如果一个函数在执行的过程中,抛出的异常在本函数内就被catch块捕获并处理了,那么该异常就不会抛给这个函数的调用者(也称“上一层的函数”);如果异常在本函数中没被处理,就会被抛给上一层的函数。

#include 
#include 
using namespace std;
class CException
{
	public :
		string msg;
		CException(string s):msg(s) { }
};
double Devide(double x, double y)
{
	if(y == 0)
		throw CException("devided by zero");//这个异常就要被返回到上一层函数,让程序员看到
	cout << "in Devide" << endl;
	return x / y;
}
int CountTax(int salary)
{
	try {
		if( salary < 0 )
			throw -1;
		cout << "counting tax" << endl;
	}
	catch (int ) {//异常在函数内部就被处理掉了
		cout << "salary < 0" << endl;
	}
	cout << "tax counted" << endl;
	return salary * 0.15;
}
int main()
{
	double f = 1.2;
	try {
		CountTax(-1);
		f = Devide(3,0);//Devide函数内部遇到异常,直接跳出,赋值语句不会被执行
		cout << "end of try block" << endl;
	}
	catch(CException e) {
		cout << e.msg << endl;
	}
	cout << "f=" << f << endl;
	cout << "finished" << endl;
	return 0;
}
输出结果:
salary < 0
tax counted
devided by zero
f=1.2
finished

C++标准异常类

C++标准库中有一些类代表异常,这些类都是从exception类派生而来

bad_cast
在用 dynamic_cast进行从多态基类对象(或引用),到派生类的引用的强制类型转换时,如果转换是不安全的,则会抛出此异常。

#include 
#include 
#include 
using namespace std;
class Base
{
	virtual void func(){}
};
class Derived : public Base
{
	public:
		void Print() { }
};
void PrintObj( Base & b)
{
	try {
		Derived & rd = dynamic_cast<Derived&>(b);//此转换若不安全,会抛出bad_cast异常
		rd.Print();
	}
	catch (bad_cast& e) {
		cerr << e.what() << endl;
	}
}
int main ()
{
	Base b;
	PrintObj(b);
	return 0;
}
输出结果:
Bad dynamic_cast!

bad_alloc
在用new运算符进行动态内存分配时,如果没有足够的内存,则会引发此异常。

#include 
#include 
using namespace std;
int main ()
{
	try {
		char * p = new char[0x7fffffff];//无法分配这么多空间,会抛出异常
	}
	catch (bad_alloc & e) {
		cerr << e.what() << endl;
	}
	return 0;
}
输出结果:
bad allocation

out_of_range
用vector或string的at成员函数根据下标访问元素时,如果下标越界,就会抛出此异常。[]也可以根据下标访问元素,速度比at成员函数快,但是遇到越界时不会抛出异常。例如:

#include 
#include 
#include 
#include 
using namespace std;
int main ()
{
	vector<int> v(10);
	try {
		v.at(100)=100; //抛出out_of_range异常
	}
	catch (out_of_range& e) {
		cerr << e.what() << endl;
	}
	string s = "hello";
	try {
		char c = s.at(100); //抛出out_of_range异常
	}
	catch (out_of_range& e) {
		cerr << e.what() << endl;
	}
	return 0;
}
输出结果:
invalid vector<T> subscript
invalid string position

运行时类型检查

C++运算符typeid是单目运算符,可以在程序运行过程中获取一个表达式的值的类型。typeid运算的返回值是一个type_info类的对象,里面包含了类型的信息。
typeid和type_info用法示例

#include 
#include  //要使用typeinfo,需要此头文件
using namespace std;
struct Base { }; //非多态基类
struct Derived : Base { };
struct Poly_Base {virtual void Func(){ } }; //多态基类
struct Poly_Derived: Poly_Base { };
int main()
{
	//基本类型
	long i; int * p = NULL;
	cout << "1) int is: " << typeid(int).name() << endl;
	//输出 1) int is: int
	cout << "2) i is: " << typeid(i).name() << endl;
	//输出 2) i is: long
	cout << "3) p is: " << typeid(p).name() << endl;
	//输出 3) p is: int *
	cout << "4) *p is: " << typeid(*p).name() << endl ;
	//输出 4) *p is: int
	//非多态类型
	Derived derived;
	Base* pbase = &derived;
	cout << "5) derived is: " << typeid(derived).name() << endl;
	//输出 5) derived is: struct Derived
	cout << "6) *pbase is: " << typeid(*pbase).name() << endl;
	//输出 6) *pbase is: struct Base
	cout << "7) " << (typeid(derived)==typeid(*pbase) ) << endl;
	//输出 7) 0
	//多态类型
	Poly_Derived polyderived;
	Poly_Base* ppolybase = &polyderived;
	cout << "8) polyderived is: " << typeid(polyderived).name() << endl;
	//输出 8) polyderived is: struct Poly_Derived
	cout << "9) *ppolybase is: " << typeid(*ppolybase).name() << endl;
	//输出 9) *ppolybase is: struct Poly_Derived
	cout << "10) " << (typeid(polyderived)!=typeid(*ppolybase) ) << endl;
	//输出 10) 0
}

课程全部结束!!!!!!!!!!!!!!

你可能感兴趣的:(中国大学MOOC程序设计与算法(三):C++ 面向对象程序设计 第十周 C++11新特性和C++高级主题 笔记 之 异常处理)