异常,try catch ,throw的介绍与运用

1.C++11的处理方式

主要分为异常的抛出与捕获两步.

throw(是一个关键字),用于抛出一个对象,与try catch一起使用.

cpp 复制代码
void func()
{
	int t; cin >> t;
	if (t == 0)
	{
		string s = "throw\n";
		throw s;
	}
}

int main()
{
	while (1)
	{
		try
		{
			func();
			cout << "No throw" << endl;
		}
		catch (string& a)
		{
			cout << a << endl;
		}
	}
	return 0;
}

当throw发生时,其会沿着函数链直至找到离其最近的类型匹配的catch并运行内部代码(catch形参的类型要与throw对象类型匹配(这里与函数传参类似,不匹配的函数就直接销毁并走到下一个函数))(除了到catch匹配的函数才会往下运行代码,其余直接离开的函数就直接销毁且不会运行内部的代码)

总结:throw是写在try catch的try内部的(包含的函数内部的throw也是一部分),当触发throw时,其会储存一个对象(表面是一个函数中的局部变量,但实际是拷贝了一份,用这份拷贝往其他函数中走的),带着这个对象从当前函数的catch匹配,匹配就走这个catch,不匹配就通过函数链往后面的函数中的catch找直至匹配为止。

2.栈展开

就是找匹配的catch的过程,当到了mian函数也没有匹配的catch时就调用标准库的terminate函数直接中止程序(就是报error弹窗).

cpp 复制代码
try 
{//..}
catch(//../)
//...就叫收纳所有的类型,用于兜底防止程序崩毁(此处也说明了多个catch可以对应同一个try)
catch(...)
{//...}

一般要求throw出来的对象类型与catch类型完美匹配,但也有一些例外:

(1)数组与指向数组元素的指针

(2)函数与函数指针

(3)子类与指向父类的类型转换(即多态)

3.应用

(1)捕获基类(异常throw出来的是一个基类,该基类与其的派生类都是用于报异常的(因此也叫异常类))

cpp 复制代码
struct gal
{
	virtual void func()
	{
		cout << "gal error" << endl;
	}
};

struct rewrite :gal
{
	virtual void func()
	{
		cout << "rewrite error" << endl;
	}
};

struct kauakaua :gal
{
	virtual void func()
	{
		cout << "kauakaua error" << endl;
	}
};

void print()
{
	if (rand() % 7 == 0)
		throw kauakaua();
	else if (rand() % 5 == 0)
		throw rewrite();
	else if (rand() % 4 == 0)
		throw gal();
	else cout << "cute" << endl;
}

int main()
{
	srand(time(NULL));
	while (1)
	{
		Sleep(100);
		try
		{
            //封装下的不同throw代表不同的异常
			print();
		}
            //基类来获取不同派生类
		catch(gal& s)
		{
			s.func();
		}
	}
	return 0;
}

总结有:基类为原始报异常并创建虚函数(目的是形成多态),该多态就可以调用不同的虚函数从而产生不同的报异常。

(2)异常的重新抛出

应用中会对每一个异常类进行编号,目的是对不同的异常来进行更精细的处理划分。

cpp 复制代码
throw;

只有一个throw时需要已经接收到异常,因此该代码就是用于catch中写的,此处的throw可以让前面被throw的变量throw出当前的catch并进入后面函数链的catch的匹配中。

cpp 复制代码
struct gal
{
	gal(int a = 6969)
		:id(a)
	{ }
	virtual void func()
	{
		cout << "gal error" << endl;
	}
	virtual int getid() { return id; }
	int id;
};

struct rewrite :gal
{
	rewrite(int a = 0720)
		:gal(a)
	{ }
	virtual void func()
	{
		cout << "rewrite error" << endl;
	}
	virtual int getid() { return gal::id; }
	//int id = 0720;
};

struct kauakaua :gal
{
	kauakaua(int a = 0721)
		:gal(a)
	{ }
	virtual void func()
	{
		cout << "kauakaua error" << endl;
	}
	virtual int getid() { return gal::id; }
	//int id = 0721;
};

void print()
{
	if (rand() % 7 == 0)
		cout << "cute" << endl;
	else if (rand() % 5 == 0)
		throw rewrite(0720);
	else if (rand() % 4 == 0)
		throw gal(6969);
	else throw kauakaua(0721);
}

void _print()
{
	//当kauakaku为异常时,想让kauakaua的异常走多3次直到正常
	for (int a = 1; a <= 4; a++)
	{
		try
		{
			print();
			//如果正常运行就走到这里离开函数
			break;
		}
		catch (gal& g)
		{
			if (g.getid() == 0721)
			{
				//还不正常就throw离开进行异常报错
				if (a == 4)throw;
				cout << "再次生成" << a <<"次" << endl;
			}
			else
			{
				//其他类型的异常直接throw,不进行操作
				throw;
			}
		}
	}
}


int main()
{
	srand(time(NULL));
	while (1)
	{
		Sleep(500);
		try
		{
            //封装的这一层是为了对kauakaua的异常进行特殊处理
			_print();
		}
		catch(gal& s)
		{
			s.func();
		}
	}
	return 0;
}

这里的本质就是提前捕获异常对其进行操作罢了。

4.异常规范

异常的安全问题在于资源可能没有完全释放。

noexcept:

(1)作为关键字时来说明这个函数内部没有异常,如果声明了异常的函数抛出了异常,程序会调用terminate来终止程序运行。

cpp 复制代码
void func() noexcept
{}

(2)作为函数时检测该函数(严格来说是所有的表达式)内部是否有异常,可能有异常就返回0(她并不运行该表达式,只是扫一下代码看看是否有try catch throw这些关键字而已),没有返回1.

(3)标准库中有自己的异常类exception。

相关推荐
我变成萤火虫1 小时前
河南萌新联赛2026第(四)场:南阳理工学院
数据结构·c++·算法·贪心算法·stl·动态规划
To_OC3 小时前
LC 239 滑动窗口最大值:从暴力超时到单调队列一遍过
javascript·算法·leetcode
.道阻且长.4 小时前
9.LeetCode算法习题讲解--滑动窗口--无重复字符的最长字串
算法·leetcode·职场和发展·哈希算法
ltl6 小时前
HNSW:图索引如何击败树索引
算法
曹牧7 小时前
C#:数字的定义和表示方式
算法·c#
Nil2088 小时前
leetcode 138随机链表的复制
算法·leetcode·链表
疯狂打码的少年9 小时前
【数据结构】图的遍历:深度优先搜索(DFS)
数据结构·笔记·算法·深度优先
-凌凌漆-10 小时前
【freertos】Task创建(v2)
java·开发语言·算法
Nil20811 小时前
leetcode 24两两交换链表中的节点
算法·leetcode·链表