异常,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。

相关推荐
其实防守也摸鱼2 小时前
ctfshow--VIP题目限免(包含原理和知识拓展)前10个
网络·算法·安全·学习笔记·ctf·泄露·web类型
自我意识的多元宇宙2 小时前
二叉树遍历方式代码解读(1递归)
java·数据结构·算法
逻辑驱动的ken2 小时前
Java高频面试考点04
java·开发语言·算法·哈希算法·散列表
_日拱一卒2 小时前
LeetCode:142环形链表Ⅱ
算法·leetcode·链表
回忆2012初秋2 小时前
C# 射线算法:判断GPS点是否在车辆工作区域内
linux·算法·c#
sali-tec2 小时前
C# 基于OpenCv的视觉工作流-章51-点查找
图像处理·人工智能·opencv·算法·计算机视觉
黎雁·泠崖2 小时前
二叉树遍历:LeetCode 144 / 94 / 145 之递归 + 分治 + 非递归
java·数据结构·算法·leetcode
凌波粒2 小时前
LeetCode--347.前 K 个高频元素(栈和队列)
java·数据结构·算法·leetcode
FluxMelodySun2 小时前
机器学习(三十二) 半监督学习-基于分歧的方法与半监督聚类
人工智能·算法·机器学习