异常调用栈_c中throw的用法「建议收藏」

异常调用栈_c中throw的用法「建议收藏」1)若有异常则通过throw操作创建一个异常对象并抛掷

异常处理的机制
抛出异常throw、检查异常try、捕获异常catch

基本原理:
把需要检测的程序放到 try 块中,把异常处理的程序放到 catch 块中。如果执行一个函数时出现异常,可以不在该函数中立即处理,而是抛出异常信息,传递给它的上一级主调函数,上一级主调函数捕获到这个信息后在进行处理。如果上一级主调函数也不处理,就逐级向上传递,如果传递到了最高一级(main函数)还不处理,最后只能异常终止程序的执行。

C++ 异常处理的机制使异常和处理可以不由同一个函数来完成,这样做的优点是使深层次的函数专注于问题求解,而不必承担处理异常的任务,减轻了深层次函数的负担,而把处理异常的任务集中到某一层次的函数中专门来解决。

异常处理的执行过程
(1)程序流程到达 try 块,然后执行 try 块内的程序块,如果没有引起异常,那么跟在 try 块后的 catch 子句都不执行,程序从最后一个 catch 子句后面的语句继续执行下去;

(2)抛出异常的时候,将暂停当前函数的执行,开始查找匹配的 catch 子句;

(3)首先检查 throw 是否在 try 内部,如果是,检查 catch 子句,看是否其中之一与抛出对象相匹配,如果找到匹配的 catch,就处理异常;如果找不到,就退出当前函数并释放局部对象,然后继续在主调函数中查找;

(4)如果找到匹配的 catch,就处理异常,如果找不到,则退出主调函数,然后继续在调用更高一级的主调函数中查找;

(5)沿着嵌套函数调用链继续向上,直到为异常找到一个 catch 子句,只要找到能够处理异常的 catch 子句,就进入该 catch 子句,并在它的处理程序中继续执行,当 catch 结束时,跳转到该 try 块的最后一个 catch 子句之后的语句继续执行;

(6)如果始终未找到与该被抛异常匹配的 catch 子句,最终 main 函数会结束执行,则运行库函数terminate将被自动调用,terminate函数的默认功能是终止程序,其缺省功能是调用abort终止程序。

例1:

#include <iostream>
using namespace std;
double division(int a, int b)
{ 
   
	if (b == 0)
	{ 
   
		throw "Division by zero condition!";//抛出异常
	}
	return (a / b);
}

int main()
{ 
   
	int x = 50;
	int y = 0;
	double z = 0;

	try { 
   
		z = division(x, y);
		cout << z << endl;//上一句在执行中如果没有抛出异常才会执行当前这句
	}
	catch (const char* msg) { 
   //抛出的异常是const char*类型的,因此接收异常的时候也要是const char*类型的
		cout << msg << endl;
	}
	catch (...) { 
   //保证以上未列举的其它可能出现的异常均被捕获
		cout << "未知异常" << endl;
	}
	system("pause");
	return 0;
}

分析:
异常在division函数中被抛掷,由于division函数本身没有对异常的处理,所以division函数的调用终止,回到main函数对division函数的调用点,又因为该调用点处于一个try子句中,其后接收const char*类型的catch子句刚好与抛出的异常类型匹配,异常在这里被捕获(这里会有参数传递的过程),然后在该子句中对异常进行处理。

例2:

#include <iostream>
using namespace std;
double division(int a, int b)
{ 
   
	if (b == 0)
	{ 
   
		throw "Division by zero condition!";//抛出异常
	}
	
	return (a / b);
}

void mycatch(int x,int y) { 
   
	try { 
   
		int z = division(x, y);
		cout << z << endl;//上一句在执行中如果没有抛出异常才会执行当前这句
	}

	catch (...) { 
   
		cout << "接受了division的异常 但是我在mycatch中没有处理,再次向上抛出" << endl;
		throw;//不带操作数的throw表达式,把异常再次向上抛给主调函数!!
	}
}

int main()
{ 
   
	int x = 50;
	int y = 0;
	double z = 0;

	mycatch(x, y);

	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述
原因是在mycatch中没有对division抛出的异常进行处理,而是又把它往上抛给了主调函数,而在main函数中找不到与被抛掷的异常所匹配的catch子句,最终main函数会结束执行。

解决方法就是在main函数中对该异常进行处理:
注意:用一个不带操作数的throw表达式throw;可以将当前正在被处理的异常再次向上抛掷,但是这样的表达式只能出现在一个catch子句中或在catch子句内部调用的函数中。并且再次抛掷的异常对象是源异常对象(不是copy副本)

不报错的程序如下:

#include <iostream>
using namespace std;
double division(int a, int b)
{ 
   
	if (b == 0)
	{ 
   
		throw "Division by zero condition!";//抛出异常
	}
	
	return (a / b);
}

void mycatch(int x,int y) { 
   
	try { 
   
		int z = division(x, y);
		cout << z << endl;//上一句在执行中如果没有抛出异常才会执行当前这句
	}

	catch (...) { 
   
		cout << "接受了division的异常 但是我在mycatch中没有处理,再次向上抛出" << endl;
		throw;//不带操作数的throw表达式,把异常再次向上抛给主调函数!!
	}
}

int main()
{ 
   
	int x = 50;
	int y = 0;
	double z = 0;

	try { 
   
		mycatch(x, y);
	}

	catch (...) { 
   
		cout << "接受了mycatch向main抛出的异常,在main中完成了对异常的处理" << endl;
		cout << "异常如下:Division by zero condition!" << endl;
	}
	cout<<"That is ok."<<endl;
	system("pause");
	return 0;
}

在这里插入图片描述

栈的解旋

定义:异常被抛出后,从进入try块(与截获异常的catch子句相对应的那个try块)起,到异常被抛出前这期间在栈上构造(且尚未析构)的所有对象都会被自动析构,析构的顺序与构造的顺序相反,这一过程称为解旋。
例:

#include <iostream>
#include <string>
using namespace std;
class MyException { 
   
private:
	string message;
public:
	MyException(const string&message):message(message){ 
   }
	~MyException(){ 
   }
	const string&getmessage()const { 
    return message; }
};
class Demo { 
   
public:
	Demo() { 
    cout << "Demo Constructor" << endl; }
	~Demo() { 
    cout << "Demo Destructor" << endl; }
};

void func()throw(MyException) { 
   //这里用到了异常接口声明,表示函数func能够且只能够抛掷MyException类或其子类类型的异常!!!
	Demo d;
	cout << "throw MyException in func()" << endl;
	throw MyException("抛出异常");
}
int main()
{ 
   
	try { 
   
		func();
	}
	catch(MyException&obj){ 
   
		cout << obj.getmessage() << endl;
	}
	system("pause");
	return 0;
}

在这里插入图片描述
可以发现,Demo类的析构函数在”抛出异常”输出前就被自动调用了,体现了栈的解旋。
C++的标准程序库异常处理

今天的文章异常调用栈_c中throw的用法「建议收藏」分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/89061.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注