在C语言里,我们传统的处理错误的方式只有两种:
但是,无论是哪种方法,最后都会面临一种问题:
C语言中的错误码都只是告诉你大致错误的类型,却不会告诉你到底哪里出错了,而且也不会告诉特别详细的错误,所以让修bug的人往往直接开摆
但是,C++中便有了新的处理错误的方式:抛异常
在C++里,如果某一个函数有自身无法处理的错误,便可以进行抛异常。抛出异常后,该函数立即终止,返回到上一层函数中;如果上一个函数也没办法处理这个异常,则接着抛出到更外一层,一直到能接收这个异常为止。
在C++中,有三个处理异常的关键字:
直接看概念非常抽象,不妨看一个例子:
void push_back(char* a,char ins, int num, int size)
{
//num代表数组元素个数,size代表数组大小
//如果数组元素个数等于数组大小,则表示数组已满,抛出异常
if (num == size)
{
throw "数组已满";
}
//如果没有抛出异常,则程序正常运行
//如果抛出了异常,则throw相当于return,往后不会继续运行
a[num] = ins;
}
void test()
{
char a[1];
try {
push_back(a, 'c', 0, 1);
}
//catch只会捕获try中的异常,然后将异常捕获到s中
//这里函数没有抛出异常,catch捕获了个寂寞,所以不走catch语句
catch (const char* s)
{
cout << s << endl;
}
try {
push_back(a, 'c', 1, 1);
}
//这里函数抛出了异常,catch捕获到异常:“数组已满”,并将异常存储到s中
//于是程序输出“数组已满”,然后该test函数继续运行
catch (const char* s)
{
cout << s << endl;
}
}
int main()
{
test();
}
但是可能会出现一种情况:catch中捕获的变量和抛出异常的变量类型不一样,那又是个啥效果?
void push_back(char* a, char ins, int num, int size)
{
if (num == size)
{
throw "数组已满";
}
a[num] = ins;
}
void test()
{
char a[1] = { 'c' };
try {
push_back(a, 'c', 1, 1);
}
//抛出和捕获的不一样
catch (int i)
{
cout << i << endl;
}
}
int main()
{
test();
}
但是,是不是只要捕获的类型和抛出的类型不一样,程序就会报错呢?也不是
void push_back(char* a, char ins, int num, int size)
{
if (num == size)
{
throw "数组已满";
}
a[num] = ins;
}
void test()
{
char a[1] = { 'c' };
try {
push_back(a, 'c', 1, 1);
}
//抛出和捕获的不一样
catch (int* i)
{
cout << i << endl;
}
//当该函数无法处理这个异常(即捕获的类型和抛出的类型不一样)
//则函数也会立即结束,抛出异常
cout << "test" << endl;
}
int main()
{
//在main函数再捕获一次
try {
test();
}
//一直到最后一个函数也无法处理这个错误,程序才会终止并报错
catch (const char* s)
{
cout << s << endl;
}
}
当本函数无法处理的时候,则会抛出异常到再外一层,然后本函数立即终止, 往复循环,一直到能完美捕获到这个异常为止
总结以上:假设我们有函数1,2,3,栈帧调用顺序为1->2->3,运行到了函数3
同时,为了避免一个try可能收到多种异常类型,catch也可以根据类型写出不同的情况:
void test()
{
try
{
//可能会返回多种异常类型
func();
}
catch (int i)
{
//...
}
catch (string s)
{
//...
}
catch (char c)
{
//...
}
//表示其他的所有情况
catch (...)
{
//...
}
}
catch在捕获到异常时,有时该函数并不会处理异常,而是在将该异常信息进行修正后,再去将异常抛到下一层,起到传球的作用
void throw_error()
{
string s="error";
throw s;
}
void func1()
{
try {
throw_error();
}
//修正异常信息,表示异常来自于函数func1
//一定要用引用,抛出的是最初的异常信息
catch (string& s)
{
s += "_from_func1";
//再抛出
throw;
}
}
void func2()
{
try {
throw_error();
}
//修正异常信息,表示异常来自于函数func2
catch (string& s)
{
s += "_from_func2";
//再抛出
throw;
}
}
int main()
{
try {
func1();
func2();
}
catch(const string& s)
{
cout << s << endl;
}
}
异常的一大缺点便是——会产生内存泄漏的问题
void func1()
{
int* a = new int[10];
if (1)
{
throw "error";
}
//如果产生异常,则a的空间不会被释放
delete[]a;
}
如果程序中产生了异常,则后面的代码段不会继续运行。一般情况下,不会出什么问题,但是如果后面进行了delete或者free,那就会出大问题——内存泄漏了。
一般来说,我们只能通过RAII来解决这一问题。但是RAII是什么?别急,在智能指针中马上就会有讲解。
C++——智能指针和RAII-CSDN博客https://blog.csdn.net/qq_74260823/article/details/135182056?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22135182056%22%2C%22source%22%3A%22qq_74260823%22%7D