【C++】Exception【Full version】

Table of contents

1. The traditional way of handling errors in C language

2. C++ exception concept

3. Abnormal use

 3.1 Throwing and catching exceptions

3.2 Exception rethrowing

 3.3 Exceptionally safe

3.4 Exception specifications

4. Customized exception system

5.Exception system of C++ standard library

6. Advantages and disadvantages of exceptions


1. The traditional way of handling errors in C language

Traditional error handling mechanism:
1. Terminate the program, such as assert , defect :It is difficult for users to accept. If a memory error occurs, the program will be terminated except 0 .
2. Return error code (some API interfaces put the error code in errno ), Defect: requires programmers to find the corresponding errors themselves. For example, the interface functions of many libraries in the system express errors by putting the error code in errno (first, after getting the error code, you need to check the error code table then we know what the error is. Secondly, if a function gets data through the return value, it will be difficult to handle when an error occurs), in practice, C language basically uses the method of returning error codes to handle errors. In some cases, the termination program is used to handle very serious errors.
One of the flaws:
If the called function stack is very deep and error codes are returned layer by layer, the whole process will be very uncomfortable. For example, several functions have a layer-by-layer calling relationship. If a problem occurs in one layer, you have to use the return value to indicate the error. Seems very cumbersome.
Regarding the issue above, C++ given Exception to solve this type of problem

2. C++ exception concept

 Exceptions are a way of handling errors. When a function finds an error that it cannot handle, it can throw an exception and let the function's Direct or indirect callers handle this error .
throw: When a problem occurs, the program will throw an exception. This is done using the throw keyword.
catch: Catch the exception via an exception handler where you want to handle the problem . The catch keyword is used to catch exceptions
Normal, there can be multiple catch for capture.
The code in the try: try block identifies the specific exception that will be activated , blocks. catch It is usually followed by one or more
If a block throws an exception, the method to catch the exception will be try and catch Keywords. Code that may throw exceptions is placed in the try block. The code in the try block is called protection code. The syntax of the try/catch statement is as follows:
try
{
 // 保护的标识代码
}catch( ExceptionName e1 )
{
 // catch 块
}catch( ExceptionName e2 )
{
 // catch 块
}catch( ExceptionName eN )
{
 // catch 块
}
1. For example, if a typical out-of-bounds error occurs, use the exception:
int main()
{
	try
	{
		vector<int> v = { 1,2,3,4,5,6 };
		for (int i = 0; i <= v.size(); ++i)
		{
			cout << v.at(i) << " ";
		}
		cout << endl;
	}
	catch (exception& e)
	{
		cout << e.what() << endl;//what告诉我们到底发生了 什么错误
	}
	
	return 0;
}

Give usError message: Invalid vector subscript

2. Exception handling can be used when dividing by 0 errors:

①. Solved by traditional methods (judgment by return value)


int mdiv(int n, int m, int& ret)
{
	if (m == 0)
	{
		return -1;
	}

	ret = n / m;
	return 0;
}

②、Exception handling

Here's the flawed exception handling:

Improve:


3. Abnormal use

 3.1 Throwing and catching exceptions

Exception throwing and matching principles:

1. The exception is caused by throwing the object, which < a i=4>The type of object determines whichcatch processing code should be activated.

2. The processing code selected by is in the call chainThe one that matches the object type and is closest to the location where the exception is thrown.

3. After an exception object is thrown, a copy of the exception object will be generated. Because the thrown exception object may be a temporary object, a copy object will be generated, and this copied temporary object will be catch and then destroy it. (The processing here is similar to the function return by value)

4. catch(...) can catch any type of exception. The problem is that we don’t know what the exception error is.

5. There is an exception to the actual matching principle of throwing and capturing. Not all types match completely. can be thrown Derived class objects are captured using base classes. This is very practical in practice and will be explained in detail below.

Matching principles for exception stack expansion in function call chains:
1. Firstly check throw itself in < /span> statement for processing. catch. If there is a match, transfer to catchInside the block, if it is to find the matching try
2. If there is no matching catch , exit the current function stack and continue on the stack of the calling function Find matching catch in .
3. If the stack of main function is reached and there is still no match, terminate the program< /span> . The above one searches for matching
The process of catch clause is called stack expansion . So in practice, we have to add a catch(...) to capture any type of exception
Normal, otherwise when an exception is not caught, the program will terminate directly.
4. After finding the matching catch clause and processing it, it will continue along Execution continues after the a> catch clause.
Here is an easy-to-understand example:
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;
}

3.2 Exception rethrowing

It is possible thata single catch cannot fully handle an exception, after performing some correction processing, we hope to hand it over to the outer calling chain function for processing. catch The exception can be re-thrown Passed to higher-level functions for processing.
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;
}

 3.3 Exceptionally safe

①. The constructor completes the construction and initialization of the object , It is best not to in the constructor Throw an exception, otherwise may result in the object being incomplete or not fully initialized
②. The destructor mainly completes the cleanup of resources , It is best not to in the destructor An exception is thrown within, otherwise may lead to resource leaks (memory leaks, handles not closed, etc. )
③, C++ exceptions often lead to resource leakage problems, such as in newnew , resulting in memory leakdelete and
exclusion, existence lock unlock No time Out of the box, it's more than the next solution. Question, 关于 RAII
Smart pointers are explained (will be updated later).

3.4 Exception specifications

①,  The purpose of the exception specification isto let function users know what exceptions the function may throw. You can follow the function with throw( type ) to list this All exception types that a function may throw.
②, function is followed by throw() , which means The function does not throw exception.
③, Ifthere is no exception interface declaration, then thisThe function can throw exceptions of any type.
// 这里表示这个函数会抛出A/B/C/D中的某种类型的异常
void fun() throw(A,B,C,D);
// 这里表示这个函数只会抛出bad_alloc的异常
void* operator new (std::size_t size) throw (std::bad_alloc);
// 这里表示这个函数不会抛出异常
void* operator delete (std::size_t size, void* ptr) throw();
// C++11 中新增的noexcept,表示不会抛异常
thread() noexcept;
thread (thread&& x) noexcept;

4. Customized exception system

In actual use, many companies willcustomize their own exception system for standardized exception management, because in a project, if everyone throws exceptions at will , then the outer caller basically has no way to use it, so in practice a set of inherited specification systems will be defined. In this way, everyonethrows inherited derived class objects, and it is enough to capture a base class
// 服务器开发中通常使用的异常继承体系
class Exception
{
public:
	Exception(const string& errmsg, int id)
		:_errmsg(errmsg)
		, _id(id)
	{}
	virtual string what() const
	{
		return _errmsg;
	}
protected:
	string _errmsg; //错误描述
	int _id; //错误码
	//stack<string> _st; //调用栈帧
};
//实际当中不同的组来处理自己的那部分
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()
{
	srand((unsigned int)time(0));
	if (rand() % 7 == 0)
	{
		throw SqlException("权限不足", 100, "select * from name = '张三'");
	}
	//throw "xxxxxx";
}
void CacheMgr()
{
	srand((unsigned int)time(0));
	if (rand() % 5 == 0)
	{
		throw CacheException("权限不足", 100);
	}
	else if (rand() % 6 == 0)
	{
		throw CacheException("数据不存在", 101);
	}
	SQLMgr();
}
void HttpServer()
{
	// ...
	srand((unsigned int)time(0));
	if (rand() % 3 == 0)
	{
		cout << rand() << " ";
		throw HttpServerException("请求资源不存在", 100, "get");
	}
	else if (rand() % 4 == 0)
	{
		cout << rand() << " ";
		throw HttpServerException("权限不足", 101, "post");
	}
	CacheMgr();
}
int main()
{
	for (size_t i = 0; i < 10; ++i)
	{
		try
		{
			HttpServer();
		}
		catch (const Exception& e) // 这里捕获父类对象就可以
		{
			// 多态调用
			cout << e.what() << endl;
		}
		catch (...)
		{
			cout << "Unkown Exception" << endl;
		}
	}
	
	return 0;
}

5.Exception system of C++ standard library

C++ provides a series of standard exceptions , We can use these standard exceptions in our programs. They are organized in a parent-child class hierarchy as follows:
Explanation : In practice, we can inherit the exception class to implement our own exception class. But in practice, many companies define their own exception inheritance system like the above. Because the C++ standard library is not designed to be easy to use. [I just want to explain that catching exceptions in catch is enough, because exception is the parent class, and its subclasses include a series of standard exceptions]
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;
}

6. Advantages and disadvantages of exceptions

Advantages of C++ exceptions:
1. The exception object has been defined. Compared with the error code method it can be clear and accurate Displays various error information, and can even include stack call information, which can help better positioning Program bug .
2. A big problem with the traditional way of returning error codes is that in the function call chain, if a deep function returns an error, then we have to return errors layer by layer. , only the outermost layer can get the error.
//1.下面这段伪代码我们可以看到ConnnectSql中出错了,先返回给ServerStart,
//ServerStart再返回给main函数,main函数再针对问题处理具体的错误。
// 2.如果是异常体系,不管是ConnnectSql还是ServerStart及调用函数出错,都不用检查,因
//为抛出的异常异常会直接跳到main函数中catch捕获的地方,main函数直接处理错误。
int ConnnectSql()
{
	// 用户名密码错误
	if (...)
		return 1;

	// 权限不足
	if (...)
		return 2;
}

int ServerStart() {
	if (int ret = ConnnectSql() < 0)
		return ret;
	int fd = socket()
	if(fd < 0)
		return errno;
}

int main()
{
	if (ServerStart() < 0)
		...

		return 0;
}
3. Many third-party libraries contain exceptions, such as boost , gtest , gmock and other commonly used libraries, then we also need to use exceptions when using them.
4. Some functions are easier to handle using exceptions. For example, the constructor has no return value, so it is inconvenient to use error codes to handle it. For example, for a function like T& operator , if pos goes out of bounds, it can only use an exception or terminate the program, and there is no way to indicate an error through a return value.
Disadvantages of C++ exceptions:
1. Exceptions will cause the execution flow of the program to jump around, and it will be very confusing, and it will jump around when an error is thrown during runtime. This will make it more difficult for us to track and debug and analyze the program.
2. Exceptions will have some performance overhead. Of course, with the fast speed of modern hardware, this impact is basically negligible.
3. C++ does not have a garbage collection mechanism, and resources need to be managed by yourself. Exceptions can easily lead to memory leaks, deadlocks and other abnormal security issues. This requires the use of RAII to handle resource management issues. The cost of learning is higher.
4. C++ The exception system of the standard library is not well defined, which leads to everyone defining their own exception system, which is very confusing.
5. Try to use exceptions as standardized as possible, otherwise the consequences will be disastrous. If exceptions are thrown at will, the users captured by the outer layer will be miserable. So there are two points in the exception specification: 1. The exception types thrown are inherited from a base class. 2. Whether and what kind of exception a function throws, use func () throw(); is standardized.
Summary : Generally speaking, exceptions have more advantages than disadvantages, so we still encourage the use of exceptions in engineering. In addition, many languages ​​are basically
Using exceptions to handle errors can also be seen as the general trend.

Guess you like

Origin blog.csdn.net/m0_74044018/article/details/134125192