c++的异常处理

本文深入探讨了C++中的异常处理,包括异常的定义、C++异常处理机制的使用,如try-catch块,以及C++17的新特性。强调了在构造和析构函数中异常处理的变化,并介绍了标准库中的异常类。同时,讨论了异常处理的顺序和资源管理问题,以及如何自定义异常类。最后,提出了C++中异常处理的争议和返回值错误处理的建议。

一、什么是异常

什么是异常?什么是错误?什么是陷阱?这个在学汇编语言时,都有过详细的说明。这里只说异常,异常,就是普通人们理解的不同于人们设想的那样的结果,就是异常。异常是程序没有按照设计而产生的结果警示。这个结果,可能是一个命令行的提示,可能是写一个文件,也可能是直接程序挂掉(有资料把这种情形不归到异常中)。
异常,首先是一种错误,有可能是逻辑上的,比如本来想输出A,结果输出了B;也有可能是控制上的,比如网络连接断开了;也有可能是单纯硬件的,诸如此类。在c++的书籍里,包括一些大牛的推荐里,对异常都不置可否。所以一般程序在代码中,看到异常控制机制的代码一般不会很多,大多还是通过返回值来处理错误的结果。

二、c++的异常处理

c++中的异常处理机制不复杂,表现形式就是:

1) 带参数名称
try { /* */ } catch (const std::exception& e) { /* */ }
2) 不带参数名称
try { /* */ } catch (const std::exception&) { /* */ }
3) 捕获任意异常
try { /* */ } catch (...) { /* */ }

当然,异常是需要在控制的代码段中抛出的:

if (...)
{
   throw x;
}

这样,就会在上面的代码中捕获这里throw的结果对象。下面看一个完整的例子:

#include <iostream>
#include <vector>
 
int main() {
    try {
        std::cout << "Throwing an integer exception...\n";
        throw 42;
    } catch (int i) {
        std::cout << " the integer exception was caught, with value: " << i << '\n';
    }
 
    try {
        std::cout << "Creating a vector of size 5... \n";
        std::vector<int> v(5);
        std::cout << "Accessing the 11th element of the vector...\n";
        std::cout << v.at(10); // vector::at() throws std::out_of_range
    } catch (const std::exception& e) { // caught by reference to base
        std::cout << " a standard exception was caught, with message '"
                  << e.what() << "'\n";
    }
 
}

在c++,异常的处理是有顺序的,一般会按照控制的具体情况逐渐向上扩展,即最精确-精确-较精确-模糊-主程序,其实也就是栈处理的顺序,越靠前的越先处理。所以在编写异常控制时,尽量也要照这个顺序来编写控制代码,以方便快速定位到相关的错误点。否则随便搞出一个异常控制了,对研判程序的错误定位没啥帮助,那这个异常控制的目的就没有了。

try {
    f();
} catch (const std::overflow_error& e) {
    // this executes if f() throws std::overflow_error (same type rule)
} catch (const std::runtime_error& e) {
    // this executes if f() throws std::underflow_error (base class rule)
} catch (const std::exception& e) {
    // this executes if f() throws std::logic_error (base class rule)
} catch (...) {
    // this executes if f() throws std::string or int or any other unrelated type
}

异常可以抛出对象也可能抛出基础类型,需要说明的是,如果是对象,其类必须是可复制的,切记。异常同样可以嵌套,目的和不嵌套没有什么不同。在一个Catch捕获一个异常后,如果觉得没有啥用,可以向外继续抛出这个异常。如果所有的异常都没有被程序自己捕获,最终会调用std::terminate()这个函数来处理。也就是前头提到的那个顺序表述。调用这个函数后,就不要再想费尽心机的想怎么办了。老实挂掉就可以了,虽然有一些小技巧可以使用,基本不推荐了,也就是说,不要再妄想恢复程序了。

三、c++17中对异常的处理

以前在面试中,经常会有人问下面的问题,在构造和析构函数中如果发生异常怎么办?有兴趣可以查查怎么办。但在c++17中,析构函数默认是noexcept,也就是说不会抛出异常。如果知道某个函数不会出现异常,或者干脆不想抛出异常,可以在函数中增加这个关键字:

int MyFunction(int args) noexcept
{
......
}

这个关键字不错,可以在实际工程上搞一搞。在c++17还可以显示动态列出异常:

void f() throw(int); // OK: function declaration
void (*pf)() throw (int); // OK: pointer to function declaration
void g(void pfa() throw(int)); // OK: pointer to function parameter declaration
typedef int (*pf)() throw(int); // Error: typedef declaration

四、标准库的异常

如同所有的语言一样,c++在标准库中也封装了std::exception这个异常类,其它有不少都继承这个类,如下图:
在这里插入图片描述

有了标准的异常,就可以直接使用,如果不太符合自己的应用,还可以继承它,在相关的函数中完善并再次应用:

class MyException:public std::exception
{
public:
   MyException(std::string_view sv = "my test exception!")
private:
   std::string s_ = "";
};

实践出真知!

五、总结

总之,在C/C++中,不推荐使用异常控制机制,这话可是大牛们说的。有一些异常,比如指针越界,只有等到释放内存时才会崩溃,异常根本也控制不住。所以在c++中,异常有点鸡肋的感觉。还不如学C,用好返回值做处理。c++中还有一个特点,一旦产生异常,大概率下整个线程甚至进程就挂了。所以对这些异常的处理,往往也起不到其它语言中的作用。这也是c++难的一个原因.
另外一个问题是,异常控制住,那么异常带来的资源管理怎么办?该不该释放还是要继续保持,这需要好好的根据上下文斟酌。c++中的RAII机制,就是一个不错的选择,包括智能指针,但程序毕竟是人写的,还是要小心。
好好把代码写好,才是王道。
在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值