C++——异常
2023-12-14 23:57:12
异常
C语言中的异常
在C语言里,我们传统的处理错误的方式只有两种:
- 手动条件判断并终止程序,比如assert断言;
- 返回错误码, 然后对照错误码找错误。
但是,无论是哪种方法,最后都会面临一种问题:
C语言中的错误码都只是告诉你大致错误的类型,却不会告诉你到底哪里出错了,而且也不会告诉特别详细的错误,所以让修bug的人往往直接开摆
但是,C++中便有了新的处理错误的方式:抛异常
C++中的异常
在C++里,如果某一个函数有自身无法处理的错误,便可以进行抛异常。抛出异常后,该函数立即终止,返回到上一层函数中;如果上一个函数也没办法处理这个异常,则接着抛出到更外一层,一直到能接收这个异常为止。
C++处理异常的方法
异常的抛出和捕获
在C++中,有三个处理异常的关键字:
- throw:当问题出现时,便会通过throw抛出异常,结束该函数并将异常抛到上一层函数
- catch:捕获异常,当异常被抛出时,catch可以接收到异常,并判断是否可以被捕获
- try:try是catch的先制条件,catch只可以捕获try中的异常
直接看概念非常抽象,不妨看一个例子:
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
- 如果没有达到异常条件,没有抛出异常,则函数和程序都正常运行
- 如果3达到异常条件,抛出了异常,则该throw相当于return,函数3立即销毁,并将异常信息返回到函数2try对应的catch中。
- 如果函数2的catch类型可以匹配,则运行catch中的内容,然后程序再继续正常运行。
- 如果函数2的catch类型无法匹配,则函数2立即销毁,将该异常信息继续向函数1抛出。
- 如果函数1的catch类型可以匹配,则函数1运行catch中的内容,然后函数1继续正常运行
- 如果函数1的catch类型无法匹配,则函数1立即销毁。如果函数1是主函数,则代表该异常无法被捕获到,程序报错
同时,为了避免一个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是什么?别急,在智能指针中马上就会有讲解。
?
文章来源:https://blog.csdn.net/qq_74260823/article/details/134912135
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!