一.C语言传统的处理错误的方式

在设计C语言的时候,我们要独立设计一些错误码进行返回

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<thread>
#include <ctime>
#include <ratio>
#include <chrono>
using namespace std;

int main()
{
	FILE* fout = fopen("Test.txt", "r");
	cout << fout << endl;
	cout << errno << endl;
	perror("fopen fail");

	return 0;
}

系统识别的错误码对应得错误信息

二. C++异常概念

三.异常的使用

1.异常的抛出和捕获

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

void Func()
{
    int len, time;
    cin >> len >> time;
    cout << Division(len, time) << endl;
    cout << len << " " << time << endl;
}

int main()
{
    try {
        Func();
    }
    catch (const char* errmsg) {
        cout << errmsg << endl;
    }
    catch(...){
        cout<<"unkown exception"<<endl;
    }

    return 0;
}

try catch没有抛出异常是不会进去的,只有我们抛出异常,才会进去,没有抛出异常,直接就进行跳过

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

void Func()
{
	int len, time;
	cin >> len >> time;

	try
	{
		cout << Division(len, time) << endl;
	}
	catch (size_t x)
	{
		cout << x << endl;
	}
	cout << "xxxxxxxxxxxxxxxxxxxxx" << endl;
}

int main()
{
	while (1)
	{
		try
		{
			Func();
		}
		catch (const string& errmsg)
		{
			cout << errmsg << endl;
		}
		catch (...)
		{
			cout << "unkown exception" << endl;
		}
	}

	return 0;
}

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

void Func()
{
	int len, time;
	cin >> len >> time;

	try
	{
		cout << Division(len, time) << endl;
	}
	catch (size_t x)
	{
		cout << x << endl;
	}
	catch (const char* errmsg)
	{
		cout << errmsg << endl;
	}

	cout << "xxxxxxxxxxxxxxxxxxxxx" << endl;
}

int main()
{
	while (1)
	{
		try
		{
			Func();
		}
		catch (const string& errmsg)
		{
			cout << errmsg << endl;
		}
		catch (...)
		{
			cout << "unkown exception" << endl;
		}
	}

	return 0;
}

如果一个捕获的都没有的话,程序就会出现异常

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

	try
	{
		cout << Division(len, time) << endl;
	}
	catch (size_t x)
	{
		cout << x << endl;
	}
	catch (const char* errmsg)
	{
		cout << errmsg << endl;
	}
	cout << "xxxxxxxxxxxxxxxxxxxxx" << endl;
}
int main()
{
	while (1)
	{
		try
		{
			Func();
		}
		catch (const string& errmsg)
		{
			cout << errmsg << endl;
		}
		catch (...)
		{
			cout << "unkown exception" << endl;
		}
	}

	return 0;
}

2.捕获任意的异常

try
		{
			Func();
		}
		catch (const string& errmsg)
		{
			cout << errmsg << endl;
		}
		catch (...)
		{
			cout << "unkown exception" << endl;
		}

我们一般是不会抛一些字符串,整性啥的(和C语言本质上没啥改变),而是抛出一个自定义类型(包含信息错误描述 + 编号等)

我们还可以通过日志等来进行操作

3.异常不受隐式类型转换的影响

如果我们抛出上面这种异常,我们来进行接收(不能走隐式类型转换)

类型匹配一定要严格匹配,不能进行隐式类型转换

4.捕获异常不一定要类型完全一样

下面有一个案例(  服务器开发中通常使用的异常继承体系  ):

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<thread>
#include <ctime>
#include <ratio>
#include <chrono>
using namespace std;


// 服务器开发中通常使用的异常继承体系
class Exception
{
public:
	Exception(const string& errmsg, int id)
		:_errmsg(errmsg)
		, _id(id)
	{}

	virtual string what() const
	{
		return _errmsg;
	}

	int getid() const
	{
		return _id;
	}

protected:
	string _errmsg;
	int _id;
};

class SqlException : public Exception
{
public:
	SqlException(const string& errmsg, int id, const string& sql)
		:Exception(errmsg, id)
		, _sql(sql)
	{}

	virtual string what() const
	{
		string str = "SqlException:";
		str += _errmsg;
		str += "->";
		str += _sql;
		return str;
	}
private:
	const string _sql;
};

class CacheException : public Exception
{
public:
	CacheException(const string& errmsg, int id)
		:Exception(errmsg, id)
	{}

	virtual string what() const
	{
		string str = "CacheException:";
		str += _errmsg;
		return str;
	}
};

class HttpServerException : public Exception
{
public:
	HttpServerException(const string& errmsg, int id, const string& type)
		:Exception(errmsg, id)
		, _type(type)
	{}

	virtual string what() const
	{
		string str = "HttpServerException:";
		str += _type;
		str += ":";
		str += _errmsg;
		return str;
	}

private:
	const string _type;
};

void SQLMgr()
{
	if (rand() % 7 == 0)
	{
		throw SqlException("权限不足", 100, "select * from name = '张三'");
	}
	
	cout << "调用成功" << endl;
}

void CacheMgr()
{
	if (rand() % 5 == 0)
	{
		throw CacheException("权限不足", 100);
	}
	else if (rand() % 6 == 0)
	{
		throw CacheException("数据不存在", 101);
	}
	SQLMgr();
}

void seedmsg(const string& s)
{
	//cout << "void seedmsg(const string& s)" << endl;

	//throw HttpServerException("网络不稳定,发送失败", 102, "put");

	if (rand() % 2 == 0)
	{ 
		throw HttpServerException("网络不稳定,发送失败", 102, "put");
	}
	else if (rand() % 3 == 0)
	{
		throw HttpServerException("你已经不是对象的好友,发送失败", 103, "put");
	}
	else
	{
		cout << "发送成功" << endl;
	}
}

void HttpServer()
{
	/*if (rand() % 3 == 0)
	{
		throw HttpServerException("请求资源不存在", 100, "get");
	}
	else if (rand() % 4 == 0)
	{
		throw HttpServerException("权限不足", 101, "post");
	}*/
	
	// 失败以后,再重试3次
	for (size_t i = 0; i < 4; i++)
	{
		try
		{
			seedmsg("今天一起看电影吧");
			break;
		}
		catch (const Exception& e)
		{
			if (e.getid() == 102)
			{
				if (i == 3)
					throw e;

				cout << "开始第" << i+1 << "重试" << endl;
			}
			else
			{
				throw e;
			}
		}
	}
	

	CacheMgr();
}


int main()
{
	srand(time(0));

	while (1)
	{
		this_thread::sleep_for(chrono::seconds(1));

		try 
		{
			HttpServer();
		}
		catch (const Exception& e) // 这里捕获父类对象就可以
		{
			using std::chrono::system_clock;
			// 多态
			system_clock::time_point today = system_clock::now();
			std::time_t tt = system_clock::to_time_t(today);
			cout << ctime(&tt) << e.what() << endl << endl;
		}
		catch (...)
		{
			cout << "Unkown Exception" << endl;
		}
	}

	return 0;
}

上面那个样例,有两个点要进行理解:

1.我们在不同的模块,可以抛出对应得错误,这样我们能根据抛出错误得信息进行分析

2.我们在接收到异常的时候,我们不想处理,我们可以再次进行抛出

四.异常的安全

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


void Func()
{
	// 这里可以看到如果发生除0错误抛出异常,另外下面的array没有得到释放。
	// 所以这里捕获异常后并不处理异常,异常还是交给外面处理,这里捕获了再
	// 重新抛出去。
	// 
	// 更好解决方案是智能指针
	int* array1 = new int[10];
	int* array2 = new int[10];   

		int len, time;
		cin >> len >> time;
		cout << Division(len, time) << endl;


	// ...
	cout << "delete []" << array1 << endl;
	delete[] array1;

	cout << "delete []" << array2 << endl;
	delete[] array2;
}


int main()
{
    try
    {
        Func();
    }
    catch (const char* errmsg)
    {
        cout << errmsg << endl;
    }
    return 0;
}

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

void Func()
{
	// 这里可以看到如果发生除0错误抛出异常,另外下面的array没有得到释放。
	// 所以这里捕获异常后并不处理异常,异常还是交给外面处理,这里捕获了再
	// 重新抛出去。
	int* array = new int[10];

	try 
	{
		int len, time;
		cin >> len >> time;
		cout << Division(len, time) << endl;
	}
	catch (...)
	{
		cout << "delete []" << array << endl;
		delete[] array;

		throw; // 异常重新抛出,捕获到什么抛出什么
	}

	// ...
	cout << "delete []" << array << endl;
	delete[] array;
}



int main()
{
    try
    {
        Func();
    }
    catch (const char* errmsg)
    {
        cout << errmsg << endl;
    }
    return 0;
}

这个地方不是为了将我们捕获到的异常进行处理,而是为了解决我们的内存泄漏问题

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


void Func()
{
	// 这里可以看到如果发生除0错误抛出异常,另外下面的array没有得到释放。
	// 所以这里捕获异常后并不处理异常,异常还是交给外面处理,这里捕获了再
	// 重新抛出去。
	// 
	// 更好解决方案是智能指针
	int* array1 = new int[10];
	int* array2 = new int[10];   // 抛异常呢

	try
	{
		int len, time;
		cin >> len >> time;
		cout << Division(len, time) << endl;
	}
	catch (...)
	{
		cout << "delete []" << array1 << endl;
		cout << "delete []" << array2 << endl;

		delete[] array1;
		delete[] array2;

		throw; // 异常重新抛出,捕获到什么抛出什么
	}

	// ...
	cout << "delete []" << array1 << endl;
	delete[] array1;

	cout << "delete []" << array2 << endl;
	delete[] array2;
}


int main()
{
    try
    {
        Func();
    }
    catch (const char* errmsg)
    {
        cout << errmsg << endl;
    }
    return 0;
}

上面这段程序还是会存在我们的异常:

所以,我们的new也是会进行抛异常的,但是这样我们代码写起来就不优雅了(太冗余了)

后续我们可以使用智能指针来解决这种问题

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


struct A
{
	~A() noexcept
	{
		cout << "~A()" << endl;
	}

	int _a1 = 0;
	int _a2 = 0;
};

template<class T>
class SmartPtr
{
public:
	// RAII
	SmartPtr(T* ptr)
		:_ptr(ptr)
	{}

	~SmartPtr()
	{
		delete _ptr;
	}

	// 重载运算符,模拟指针的行为
	T& operator*()
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr;
	}
private:
	T* _ptr;
};

void Func()
{
	// 更好解决方案是智能指针
	shared_ptr<A> sp1(new A);
	shared_ptr<A> sp2(new A);
	sp1->_a1++;
	sp1->_a2++;

	shared_ptr<A> sp3(sp1);

	int len, time;
	cin >> len >> time;
	cout << Division(len, time) << endl;
}

int main()
{
	try
	{
		Func();
	}
	catch (const char* errmsg)
	{
		cout << errmsg << endl;
	}
	catch (const exception& e)
	{
		cout << e.what() << endl;
	}
	catch (...)
	{
		cout << "未知异常"<< endl;
	}

	return 0;
}

五.异常规范

noexcept  关键字:

确定不抛异常的,就不用加  noexcept  关键字

六.自定义异常体系

七.C++标准库的异常体系

int main()
{
    try{
        vector<int> v(10, 5);
        // 这里如果系统内存不够也会抛异常
        v.reserve(1000000000);

        // 这里越界会抛异常
        v.at(10) = 100;
    }
    catch (const exception& e) // 这里捕获父类对象就可以
    {
        cout << e.what() << endl;
    }
    catch (...)
    {
        cout << "Unkown Exception" << endl;
    }

    return 0;
}

如 out_of_range() 的使用:

八.异常的优缺点

1.优点

2.缺点

总结:

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐