C++Primer_笔记_异常处理

异常,当一个函数发现一个无法处理的错误时抛出异常,让函数的

调用者直接或间接的处理这个问题。

异常处理,是一种允许两个独立开发的程序组件在程序执行期间遇到程序不正常的

情况(称为异常,exception )时相互通信的机制。

异常抛出:

throw (表达式)

异常发现与抛出异常:

/*发现异常并且抛出异常*/
try
{
    //可能出现异常的语句
}

捕获异常:

catch(类型名 + 形参名)    //捕获特定类型异常
{
    
}
catch(...)           //捕获任意类型异常(在不确定异常类型时使用) 
{

}

下来看一个简单的例子:

#include<iostream>
using namespace std;

int Div(int a, int b)
{
    return a/b;
}

int main()
{
    Div(1,0);
    system("pause");
    return 0;
}

结果显示:

程序直接崩溃,由于调用时传入第二个参数是零,零不能作除数,所以会崩溃,下面使用异常处理这个问题:

#include <iostream>
#include  <string>
using namespace std;

int Div(int a, int b)
{
    if(b == 0)
    {
	throw string("parameter error.");//异常抛出
    }
    return a/b;
}
int main()
{
    try
    {
    Div(1, 0);//发现异常,与抛出异常
    }
    catch(const string& S)
    {
    	cout<<S<<endl;//捕获异常
    }
    system("pause");
    return 0;
}

程序可以正常运行,也可以看到异常出现的地方,这样就可以很清楚的解决这个问题。

下面再看个了例子:

#include <iostream>
#include <string>
using namespace std;

void test()
{
    int* p = new int(1);
    if(1)
    {
	throw string("error.");
    }
    delete p;
}

int main()
{
    try
    {
	test();
    }
    catch(const string& S)
    {
	cout<<S<<endl;
    }
    system("pause");
    return 0;
}

结果显示:

这个程序运行貌似没有问题,其实问题大了。

void test()
{
    int* p = new int(1);
    if(1)
    {
	throw string("error.");
	//程序执行到这里,直接就去catch()那块了,
	//导致new出来的内存没有释放,造成内存泄漏。
    }
    delete p;
}

内存泄漏:

会导致你开辟出来的那块内存以后就不可以用了,这样多泄漏几次,你的电脑就哈哈了,会很卡,很卡。

内存泄漏危害:

从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在。真正有危害的是内存泄漏的堆积,这会最终消耗尽系统所有的内存。从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积,而隐式内存泄漏危害性则非常大,因为较之于常发性和偶发性内存泄漏它更难被检测到。

当然,这里并不是说内存泄漏,下面可以用异常来处理上面这个问题:

#include <iostream>
#include <string>
using namespace std;

void test()
{
	int* p = new int(1);
	try//发现异常
	{
		if(1)
		{
			throw string("error.");//异常抛出
		}
	}
	catch(...)//捕获异常
	{
		delete p;//释放开辟的空间
		throw;//异常重新抛出
	}
	delete p;
}

int main()
{
	try//发现重新抛出的异常
	{
		test();
	}
	catch(const string& S)//捕获异常
	{
		cout<<S<<endl;
	}
	system("pause");
	return 0;
}

结果显示:

这样就不会出现内存泄漏,也会很清楚的在控制台显示错误的信息。

异常重新抛出:在异常处理过程中也可能存在单个catch 子句不能完全处理异常的情况。在某些修

正动作之后,catch 子句可能决定该异常必须由函数调用链中更上级的函数来处理那么catch子句可以通过重新抛出(rethrow )该异常把异常传递给函数调用链中更上级的另一个catch子句,rethrow 表达式的形式为:

throw;

rethrow 表达式重新抛出该异常对象rethrow 只能出现在catch 子句的复合语句中。被重新抛出的对象就是其原来的异常对象。

栈展开:

在查找用来处理被抛出异常的catch 子句时因为异常而退出复合语句和函数定义这个过程

抛出异常的时候,将暂停当前函数的执行,开始查找对应的匹配catch语句。首先检查throw本身是否在catch块内部,如果是,再查找匹配的catch语句。如果有匹配的,则处理。没有则退出当前函数栈,继续在调用函数的栈中进行查找。不断重复上述过程。若到达main函数的栈,依旧没有匹配的,则终止程序。

上述这个沿着调用链查找匹配的catch语句的过程称为栈展开。找到匹配的catch语句并处理以后,会继续沿着catch语句后继继续执行。

异常捕获的匹配规则:

异常对象的类型与catch说明符的类型必须完全匹配。

只有以下几种情况例外:

1. 允许从非const对象到const的转换。

2. 允许从派生类型到基类类型的转换。

3. 将数组转换为指向数组类型的指针,将函数转换为指向函数类型的指针。

异常处理总结:

异常处理就是通过throw,try,catch,这三个关键字,来发现异常,并抛出异常,捕获异常,可以做出相应的处理。(这只是简单的异常处理)

时间: 2024-08-09 13:46:48

C++Primer_笔记_异常处理的相关文章

python 学习笔记 6 -- 异常处理

当你的程序中出现某些 异常的 状况的时候,异常就发生了.例如,当你想要读某个文件的时候,而那个文件不存在.或者在程序运行的时候,你不小心把它删除了. 那么如果你是在IDE中运行,一个错误发生,异常会被打引出来,这便是未处理异常:当异常发生时,如果没有代码去关注和处理它,这些异常会传给置在Python中的缺省处理,他会输出一些调试信息并且终止运行.如果是在IDE中,这不是什么大事,但是如果是Python程序运行中的异常,它会导致整个程序终止,对于这些情况可以使用异常来处理. 1.try..exce

Effective C++_笔记_条款08_别让异常逃离析构函数

(整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) C++并不禁止析构函数吐出异常,但它不鼓励你这样做.考虑如下代码: 1: class Widget{ 2: public: 3: ... 4: ~Widget() {...} //假设这个可能吐出一个异常 5: }; 6:  7: void doSomething() 8: { 9: vector<Widget> v ; //v在这里被自动销毁 10: ...

Oracle 学习笔记 17 -- 异常处理(PL/SQL)

程序在执行过程中出现异常是正常的,在程序的编写过程中出现异常也是不可避免的.但是要有相应的异常处理的机 制,来保证程序的正常执行.PL/SQL程序执行过程中出现的错误,称为异常.一个优秀的程序都应该能够正确处理 各种出错的情况,并尽可能的从错误中恢复.PL/SQL提供了异常处理机制. 概念: 异常处理(exception)是用来处理正常执行过程中未预料的事件,程序块的异常处理定义的错误和自定义的错误, 由于PL/SQL程序块一旦产生异常而没有指出如何处理时,程序就会异常的终止. 有三种类型的错误

python学习笔记十——异常处理

1.try: command except 错误类型,记录错误信息变量: command finally: command try...finally的用处是无论是否发生异常都要确保资源释放代码的执行.一般来说,如果没有发生错误,执行过try语句块之后执行finally语句块,完成整个流程.如果try语句块发生了异常,抛出了这个异常,此时就马上进入finally语句块进行资源释放处理.如下从几个细节讨论finally的特性. 1).try中的return: 当在try语句块中含有return语句

十五、Android学习笔记_授权过程

1.需要申请App Key和App Secret.不同的开发平台有不同的接入方式,可以参考文档,然后将这两个值放进去. 2.通过OAuth类实现认证,它会自动跳转到认证界面,进行授权,成功之后需要处理回调接口. 3.在第二步调用回调接口时,它会返回用户的基本信息,比如用户id.此时需要将用户id信息保存起来,为后面登录做准备.回调接口的写法就为myapp://AuthorizeActivity,其中scheme全部为小写字母. <activity android:name="com.wei

九、Android学习笔记_ Android开发中使用软引用和弱引用防止内存溢出

在<Effective Java 2nd Edition>中,第6条"消除过期的对象引用"提到,虽然Java有 垃圾回收机制,但是只要是自己管理的内存,就应该警惕内存泄露的问题,例如的对象池.缓存中的过期对象都有可能引发内存泄露的问题.书中还提到可以用 WeakHashMap来作为缓存的容器可以有效解决这一问题.之前也确实遇到过类似问题,但是没有接触过"弱引用"相关的问题,于是查阅了一些资料. <Java 理论与实践: 用弱引用堵住内存泄漏>

Effective C++_笔记_条款12_复制对象时勿忘其每一个成分

(整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 编译器会在必要时候为我们的classes创建copying函数,这些“编译器生成版”的行为:将被烤对象的所有成员变量都做一份拷贝. 如果你声明自己的copying函数,意思就是告诉编译器你并不喜欢缺省实现中的某些行为.编译器仿佛被冒犯似的,会以一种奇怪的方式回敬:当你的实现代码几乎必然出错时却不告诉你.所以自己实现copying函数时,请遵循一条规则:如果你为c

Effective C++_笔记_条款11_在operator=中处理“自我赋值”

(整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 为什么会出现自我赋值呢?不明显的自我赋值,是“别名”带来的结果:所谓“别名”就是“有一个以上的方法指涉对象”.一般而言如果某段代码操作pointers或references而它们被用来“指向多个相同类型的对象”,就需要考虑这些对象是否为同一个.实际上两个对象来自同一个继承体系,它们甚至不需要声明为相同类型就可能造成“别名”.因为一个base class的refe

Effective C++ 阅读笔记_条款27 尽量少做转型动作

Effective C++ 阅读笔记_条款27 尽量少做转型动作 1.转型的三种形式,可以分为两大类. (1)旧式转型(old-style casts) (1.1) (T) expresstion (1.2) T (expression) (2) 新式转型(c++-style casts) (2.1)const_cast<T> (expression) (2.2)dynamic_cast<T> (expression) (2.3)reinterpret_cast<T>