C++中的异常

51 阅读1分钟

C++中的异常主要由三个关键字构成:try,catch,throw。 其中throw抛出异常,try包围可能发生异常的语句,catch用来做异常处理。 看一个例子:

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

int main()
{
	try {
		throw(string("gaoyuelong"));
	}
	catch (string str) {
		cout << str << endl;
	}

	return 0;
}

throw可以抛出任意异常,只要catch进行相应的捕获即可。 比如:

int main() {
	int age = 100;
	try {
		if (age > 90) {
			throw(age);
			//throw后面的语句不会被执行到
			cout << "123" << endl;
		}
	}
	catch (int age) {
		cout << age << " is too big" << endl;
	}
	return 0;
}

上面的例子中提到throw后面的语句不会被执行值得注意。

异常会向外传播,直到有相应的catch捕获异常,比如:

void inner()
{
	//在外层被捕获
	throw("123");
}

void outer()
{
	try
	{
		inner();
	}
	catch (...)
	{
		cout << "exception caught" << endl;
	}
}

int main()
{
    outer();
    return 0;
}

如果一直向外传播都没有找到匹配的catch捕获,程序会崩溃。catch(...)表示捕获任意类型的异常。

如果我们想在抛出异常时,携带一些信息,帮助开发人员更好的调用调试,可以继承自exception类,重写what方法:

class idontknowwhatshappening : public exception
{
public:
	idontknowwhatshappening() = default;
	~idontknowwhatshappening() = default;

	idontknowwhatshappening& operator=(const idontknowwhatshappening&) = default;
	//返回const char*而不是string,string可能也会发生异常
	const char* what() { return "I don't know what's happening"; };
};

void inner()
{
	try
	{
		throw(idontknowwhatshappening());
	}
	catch (idontknowwhatshappening &e)
	{
		cout << e.what() << endl;
	}
}

void outer()
{
	try
	{
		inner();
	}
	catch (...)
	{
		cout << "exception caught" << endl;
	}
}

异常被捕获时,会按照catch定义的语句顺序,挨个进行匹配,执行第一个匹配完成的流程:

class idontknowwhatshappening : public exception
{
public:
	idontknowwhatshappening() = default;
	~idontknowwhatshappening() = default;

	idontknowwhatshappening& operator=(const idontknowwhatshappening&) = default;

	const char* what() const { return "I don't know what's happening"; };
};

void inner()
{
	try
	{
		throw(idontknowwhatshappening());
	}
	catch (const idontknowwhatshappening &e)
	{
                //匹配到则先执行这里
		cout << e.what() << endl;
	}
	catch (exception &e)
	{
		cout << "e" << endl;
	}
    //而如果父类exception先进行捕获的话,就先执行父类的捕获
    //catch按顺序执行,他会执行第一个能匹配到的捕获
    /*catch (exception &e)
	{
		cout << "e" << endl;
	}
    catch (const idontknowwhatshappening &e)
	{
		cout << e.what() << endl;
	}*/
}

如果异常在被第一个catch捕获时,需要修改对象的值,这时需要引用对象:

class i : public exception
{
public:
	i() = default;
	~i() = default;
	i& operator=(const i&) = default;
	const char* what() { return "exception i"; }
	string s;
};

void outer()
{
	try
	{
		throw i();
	}
	catch (i& I)//这里使用引用
	{
		I.s = "123";
		cout << "exception caught" << endl;
		cout << I.s << endl;
	}
}

若当前catch发现处理不了异常,需要继续向上传递时,使用throw

void outer()
{
	try
	{
		throw i();
	}
	catch (i& I)
	{
		I.s = "123";
		cout << "exception caught" << endl;
		cout << I.s << endl;
		//继续向上层抛出
		throw;
	}
}

int main() {
	try
	{
		outer();
	}
	catch (i &I)
	{
		cout << "main: " << I.s << endl;
	}

	return 0;
}

如果一个函数我们确保他不会发生异常,可以使用noexcept关键字,这样编译器可能会对这个函数做一些不会抛出异常的优化

//用noexcept修饰后,不能再抛出异常了,否则程序会崩溃
const char* foo() noexcept
{
	throw("123");
}

int main() 
{
	
	try 
	{
		foo();
	}
	catch (...)
	{
		cout << "exception caught" << endl;
	}
	return 0;
}

关于异常的编码规范,一般关于异常的处理都放在外面做,而函数自身,只需要进行检查,若不符合规范抛出异常让外层处理。比如:

void Process(int n)
{
	if (n > 3)
	{
        //去外面处理,不要在使用方处理
		throw runtime_error("number too big");
	}
}

int main() 
{
	vector<int> v = {1, 2, 3, 4, 5};
	for (int i = 0; i < 5; i++)
	{
		try {
			Process(v[i]);
			cout << v[i] << endl;
		}
		catch(runtime_error &e)
		{
			cout << e.what() << endl;
		}
	}
	return 0;
}

以上,便是对C++中异常的一些基础使用,希望对你有用。

如果你觉得文章还不错,欢迎点赞评论哦!关注账号,你还会收到关于C++的更多知识哦!!