《C++Primer》读书笔记--异常处理

定义:

异常,让一个函数可以在发现自己无法处理的错误时抛出一个异常,希望它的调用者可以直接或者间接处理这个问题。

之前写的一些小程序,几乎没有用到过异常处理。因为规模比较小,一般的问题在函数内就加上一些判断条件解决了,一般的做法就是返回一个表示错误的值(比如返回NULL指针),在调用的时候判断一下返回的值,虽然简单,但是功能并不强大,只适合小型项目。而大型的项目,如果这么搞就乱套了,所以就要用到异常处理这一套系统。

一个最简单的异常处理:

// ConsoleApplication3.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
#include <vector>
#include <string>
using namespace std;

//异常类
class NumberException{};

void NumOption(const int& a, const int& b)
{
	try
	{
		if (b == 0)
			throw NumberException();
		cout<<a / b<<endl;
	}
	catch(const NumberException& e)
	{
		cout<<"Exception!"<<endl;
	}
}

int _tmain(int argc, _TCHAR* argv[])
{

	int a,b;
	cin>>a;
	cin>>b;
	NumOption(a, b);
	system("pause");
	return 0;
}

结果:

//输入

1

0

//输出

Exception!

关于异常处理有下面几点要注意:

1.在抛出一个异常之后,代码会转到异常处理的地方执行,throw后面的代码就不会再执行。如果没有找到相关catch,会跳出一级继续寻找。这个过程成为栈展开。所以要注意这样一种情况,在栈展开的过程中,程序块中的各种临时对象都会被销毁。

一个例子:

// ConsoleApplication3.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
#include <vector>
#include <string>
using namespace std;

//异常类
class NumberException{};

//测试的类
class Test
{
public:
	~Test()
	{
		cout<<"Test is destructed!"<<endl;
	}
};

void NumOption(const int& a, const int& b)
{

	try
	{
		Test test;
		if (b == 0)
			throw NumberException();
		cout<<a / b<<endl;
	}
	catch(const NumberException& e)
	{
		cout<<"Exception!"<<endl;
	}
}

int _tmain(int argc, _TCHAR* argv[])
{

	int a,b;
	cin>>a;
	cin>>b;
	NumOption(a, b);

	system("pause");
	return 0;
}

正常情况:

//输入

1

1

//输出

1

Test is destructed!

异常情况:

//输入

1

0

//输出

Test is destructed!

Exception!

从上面的结果我们可以看出,正常情况下,程序执行完,先输出1,再销毁Test对象。但是抛出异常时,显然还没有出块的作用域,但是异常发生了,throw后面的内容不执行,也相当于声明周期结束,所有临时对象都会被销毁。

2.通常情况下,接受异常的catch块最好设定为引用类型。如果设为值传递,那么会发生基类参数接受子类异常时削去子类特有部分的情况。

并且我们可以设定是否为const引用,决定是否可以在catch块中对异常进行修改。

3.catch的匹配原则:越专门的匹配越靠前,换句话说,如果同时有子类和基类异常处理的catch,而抛出的是子类异常,我们为了能够处理子类,需要把子类异常处理放在前面。

例子:

// ConsoleApplication3.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
#include <vector>
#include <string>
using namespace std;

//异常类
class NumberException{};
//派生的异常类
class ZeroException : public NumberException {};

void NumOption(const int& a, const int& b)
{

	try
	{
		if (b == 0)
			throw ZeroException();
		cout<<a / b<<endl;
	}

	catch(const ZeroException& e)
	{
		cout<<"ZeroException!"<<endl;
	}

	catch(const NumberException& e)
	{
		cout<<"NumberException!"<<endl;
	}
}

int _tmain(int argc, _TCHAR* argv[])
{

	int a,b;
	cin>>a;
	cin>>b;
	NumOption(a, b);

	system("pause");
	return 0;
}

结果:

1

0

//输出

ZeroException!

但是如果我们把catch(cost NumberException& e)和catch(const ZeroException& e)这两个换一下位置:结果就变成了

NumberException!

可见,要想处理更特例的异常,就要越放在前面。否则父类匹配了之后,不会再匹配子类。

4.重新抛出,当我们处理了一个异常但是没处理全时,可以将异常继续抛出。使用throw;关键字可以继续抛出。但是如果没有相应的catch继续接受异常的话,程序会terminate!

一个嵌套处理异常的例子:

// ConsoleApplication3.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
#include <vector>
#include <string>
using namespace std;

//异常类
class NumberException{};
//派生的异常类
class ZeroException : public NumberException {};

void NumOption(const int& a, const int& b)
{
	try
	{
		if (b == 0)
			throw ZeroException();
		cout<<a / b<<endl;
	}

	catch(const ZeroException& e)
	{
		cout<<"ZeroException!"<<endl;
		throw;
	}

	catch (...)
	{
		cout<<"I can solve all Exception!"<<endl;
	}
}

int _tmain(int argc, _TCHAR* argv[])
{

	int a,b;
	cin>>a;
	cin>>b;
	//嵌套异常处理
	try
	{
		NumOption(a, b);
	}
	catch(const NumberException& e)
	{
		cout<<"NumberException!"<<endl;
	}

	system("pause");
	return 0;
}

结果:

1

0

//输出

ZeroException!

NumberException!

抛出异常时,先通过ZeroException异常处理,处理后继续抛出,外面的NumberException接收。

5.使用catch(...)可以捕获所有的异常:

catch (...)
	{
		cout<<"I can solve all Exceptions!"<<endl;
	}

6.为了加强程序的可读性,使函数的用户能够方便地知道所使用的函数会抛出哪些异常,可以在函数的声明中列出这个函数可能抛出的所有异常类型,例如:

void fun() throw( A,B,C,D);

这表明函数fun()可能并且只可能抛出类型(A,B,C,D)及其子类型的异常。

如果在函数的声明中没有包括异常的接口声明,则此函数可以抛出任何类型的异常,例如:

void fun();

一个不会抛出任何类型异常的函数可以进行如下形式的声明:

void fun() thow();
时间: 2024-12-04 18:09:02

《C++Primer》读书笔记--异常处理的相关文章

C++primer读书笔记11-多态

多态也是C++中的一个重要的方面,多态和动态类型,虚函数本质上是指相同的事情. 1 虚函数 类中的成员函数原型前面加上virtual 表面这个函数是个虚函数.虚函数的目的是为了在继承它的派生类中重新定义这个函数,以便于通过基类的指针或引用在运行时对派生类的函数进行调用. 2 派生类和虚函数 派生类一般情况下要重定义所继承的虚函数,有几个注意事项. <1>虚函数的声明必须和基类中的函数声明原型完全一致,例外的是当基类返回基类型的指针或者引用的时候,派生类可以派生类类型的指针或者引用 <2&

C++中的volatile(Primer读书笔记)

时间:2014.05.24 地点:基地 -------------------------------------------------------------------------- 一.简述 volatile限定符平时很少用到,今天倒是碰到了,所幸探个明白.volatile 英文字面意思是"不稳定的",确切的计算机含义时与机器相关,所以在对包含volatile的程序在移植到新机器或使用不同的编译器时往往还需要对编译器进行一些改变. -----------------------

C++ primer读书笔记10-继承

封装,继承,多态是C++的三大基本概念,这里着重总结一下继承相关的东西 1 类派生列表 类派生列表指定派生类要继承的基类,派生列表中有一个或者多个基类如: class B : public A1,protected A2,private A3 但是单继承时最常见的,多继承不多见 2 派生类的定义 派生类继承时,会包含父类的所有成员,即便私有成员不能被访问.父类中的虚函数,在派生类中一般也要定义,如 果不定义的话,派生类将继承基类的虚函数 3 基类必须是已经定义的 一个仅仅声明的类,是不能出现在派

C++primer读书笔记9转换与类类型

有时候指定了自己类类型来表示某种类型数据如SmallInt,那么为了方便计算就会指定一个转换操作符,将该类类型在某种情况下自动的转换为指定的类型 <1>转换操作符 operator type(); 转换函数必须是类成员函数,不能指定返回类型,并且形参列表必须为空,并且通常不应该改变转换对象,所以操作符通常定义为const成员. #include <iostream> using namespace std; class SmallInt { public: SmallInt(int

C++ Primer 读书笔记整理(一)

1.读取数量不定的输入数据时可以把cin语句放到条件判断语句中,如果流的状态有效则读取成功,否则读取失败. 例如: while(cin>>value) /* do something */ 2.顶层const与底层const的区别 1)顶层const表示任意的对象是const(即常量) 例如: const int value = 0; // i是顶层const常量 int i= 0; int *const ptr = &i; //ptr是顶层const常量 2)底层const一般用在引

c++ primer读书笔记之c++11(二)

1 新的STL模板类型,std::initializer_list<T> c++11添加了initializer_list模板类型,用于提供参数是同类型情况的可变长度的参数传递机制,头文件是<initializer_list>. 其具体接口可参考cplusplus.com的介绍,地址如下:http://www.cplusplus.com/reference/initializer_list/initializer_list/?kw=initializer_list 与vector不

c++ primer读书笔记之c++11(四)

1  带有作用域的枚举 scoped-enumeration 相信大家都用过枚举量,都是不带有作用域的,在头文件中定义需要特别注意不要出现重名的情况.为了解决这种问题,c++11提供了带作用于的枚举.可以使用class EnumName定义,示例代码如下: enum {ZERO, ONE, TWO}; enum class color {RED, BLUE, GREEN}; 上面的是没有作用域的枚举定义形式,下面是带有作用域的枚举定义形式,调用带有作用域的枚举必须指定作用域,否则会编译出错. 还

c++ primer读书笔记之c++11(三)

1 =default构造函数限定符 c++11针对构造函数提供了=default限定符,可以用于显式指定编译器自动生成特定的构造函数.析构或赋值运算函数.参考代码如下: class CtorDftType { public: CtorDftType()=default; CtorDftType(const CtorDftType&)=default; CtorDftType & operator = (const CtorDftType &)=default; ~CtorDftTy

函数(C++ Primer读书笔记)

C++ Primer 第五版课后题 练习6.32 :下面的函数合法吗?如果合法,说明其功能:如果不合法,修改其中的错误并解释原因. #include <iostream> using namespace std; int &get(int *arry, int index) { return arry[index]; } int main() { int ia[10]; for (int i = 0; i != 10; ++i) get(ia, i) = i; return 0; }