一、抛异常

为什么要抛异常? 我们日常使用的软件和游戏,都时不时会出现一些bug,可能是某个按钮打不开,可能是信息发不出去,这些都可能是“异常”。 那么作为一款已发布的应用程序,我们就需要保证,即便发生了异常,也不要终止整个程序,保证应用程序的健壮性。

try catch

一般我们会在可能会发生异常的代码区尝试抛异常,也就是try的区域,当发生(检测到)异常后,就会跳转到catch区域,catch区域是为了尝试解决异常,即便无法解决,也不让异常终止整个程序,并记录日志信息方便debug。

示例代码如下

#include<iostream>
#include<exception>


void Alloc_func()
{
	void* ptr = new int[1000000000000000];
	delete ptr;
}
int main()
{
	try {
		Alloc_func();
	}
	catch(std::exception& e){
		std::cerr << "exception caught: " << e.what() << std::endl;
	}

	return 0;
}

C++异常-LMLPHP
该示例代码申请了过大的堆空间,抛出bad_alloc异常,如果不try catch,该代码则直接崩溃。

二、使用步骤

1.throw

是不是抛异常只能抛出std::exception类呢? 答案 不是的!
我们想抛什么类型就可以通过throw抛出去。

int divide(int i, int j)
{
	if (j == 0)
		throw "发生除零错误!";
	return i / j;
}

int main()
{
	try {
		int a, b;
		std::cin >> a >> b;
		divide(a , b);
	}
	catch(const char* str){
		std::cerr << "exception caught: " << str << std::endl;
	}

	return 0;
}

C++异常-LMLPHP
一旦throw后,则直接到catch区域。


那是不是只能有一个catch?
不是的,可以有多个catch区,当有多个catch时,会匹配对应类型的catch进行跳转。

int divide(int i, int j)
{
	if (j == 0)
		throw (void*)"发生除零错误!";
	return i / j;
}

int main()
{
	try {
		int a, b;
		std::cin >> a >> b;
		divide(a , b);
	}
	catch (int& rv)
	{
		std::cerr << "exit code: " << rv << std::endl;
	}
	catch(const char* str){
		std::cerr << "exception caught: " << str << std::endl;
	}
	catch (...) //可以接收所以抛出的类型
	{
		std::cerr << "unknown exception!" << std::endl;
	}
	return 0;
}

C++异常-LMLPHP

这里需要注意的是catch(…)可以接收所以抛出的类型。


那如果有多个catch对应类型符合,会匹配哪一个?
会匹配最近的那一个。

自我实现Expection

在一些大项目下,库提供的expeiction类并不能完全满足我们的需求,所以很多大公司都会自己写一套它们自己的Expection类,今天我们也来自己实现一下。

由于一般大项目需要分组进行,所以规定每个组最好是抛自己相关模块的那个异常,这里就可以使用到多态。

 //Expection.hpp
#pragma once
#include<iostream>
#include<string>

class Exception {
public:
	Exception(int errid, const std::string& msg)
		:_errid(errid)
		,_msg(msg){}

	virtual std::string what() const
	{
		return _msg;
	}

protected:
	int _errid;
	std::string _msg;
};


class loadException : public Exception
{
public:
	loadException(int errid, const std::string& msg, const std::string& load)
		:Exception(errid,msg)
		,_load(load) {}


	virtual std::string what() const
	{
		std::string str = "loadService: ";
		str += _msg;
		str += " -> ";
		str += _load;
		return str;

	}
private:
	std::string _load;
};


class encryptException : public Exception
{
public:
	encryptException(int errid, const std::string& msg, const std::string& type)
		:Exception(errid, msg)
		, _type(type) {}


	virtual std::string what() const
	{
		std::string str = "encryptService: ";
		str += _msg;
		str += " -> ";
		str += _type;
		return str;

	}
private:
	std::string _type;
};

class sendException : public Exception
{
public:
	sendException(int errid, const std::string& msg)
		:Exception(errid, msg)
		{}


	virtual std::string what() const
	{
		std::string str = "sendService: ";
		str += _msg;
		return str;
	}
};
#include"Exception.hpp"
#include<time.h>




void sendService()
{
	srand(time(nullptr));
	if (rand() % 7 == 0) throw sendException(4, "sendService exception: Network connection timeout ");
	std::cout << "Success !" << std::endl;
}

void encryptService()
{
	srand(time(nullptr));
	if (rand() % 9 == 0) throw encryptException(3, "encryptService exception","illegal character ");
	if (rand() % 12 == 0) throw encryptException(3, "encryptService exception","encrypt error ");
	sendService();
}

void loadService()
{
	srand(time(nullptr));
	if (rand() % 10 == 0) throw loadException(3, "loadService exception","load timeout ");
	encryptService();
}





int main()
{
	while (1)
	{
		try {
				std::this_thread::sleep_for(std::chrono::seconds(1)); //休眠1s
				loadService();
		}
		catch (const Exception& e) {
			std::cout << "exception caught: " << e.what() << std::endl;
		}
		catch (...)
		{
			std::cout << "exception caught: unknown exception" << std::endl;
		}
	}

	return 0;
}

C++异常-LMLPHP

异常的重新抛出

在有些场景下,有可能单个的catch不能完全处理一个异常,在进行一些校正处理以后,希望再交给更外层的调用链函数来处理,catch则可以通过重新抛出将异常传递给更上层的函数进行处理。

double Division(int a, int b)
{
	// 当b == 0时抛出异常
	if (b == 0)
	{
		throw "Division by zero condition!";
	}
	return (double)a / (double)b;
}
void Func()
{

	int* array = new int[10];
	try {
		int len, time;
		std::cin >> len >> time;
		std::cout << Division(len, time) << std::endl;
	}
	catch (...)
	{
		std::cout << "delete []" << array << std::endl;
		delete[] array;
		throw;  //这种写法用于重复抛出,捕捉到什么类型就抛什么类型
	}

	std::cout << "delete []" << array << std::endl;
	delete[] array;
}
int main()
{
	try
	{
		Func();
	}
	catch (const char* errmsg)
	{
		std::cout << errmsg << std::endl;
	}
	return 0;
}

C++异常-LMLPHP

04-02 12:44