罗剑锋的 C++ 实战笔记
罗剑锋
前奇虎 360 技术专家,Nginx/OpenResty 开源项目贡献者
34779 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 32 讲
结束语 (1讲)
罗剑锋的 C++ 实战笔记
15
15
1.0x
00:00/00:00
登录|注册

09 | exception:怎样才能用好异常?

你好,我是 Chrono。
上节课,我建议尽量不用裸指针、new 和 delete,因为它们很危险,容易导致严重错误。这就引出了一个问题,如何正确且优雅地处理运行时的错误。
实际上,想要达成这个目标,还真不是件简单的事情。
程序在运行的时候不可能“一帆风顺”,总会遇到这样那样的内外部故障,而我们写程序的人就要尽量考虑周全,准备各种“预案”,让程序即使遇到问题也能够妥善处理,保证“健壮性”。
C++ 处理错误的标准方案是“异常”(exception)。虽然它已经在 Java、C#、Python 等语言中得到了广泛的认可和应用,但在 C++ 里却存在诸多争议。
你也可能在其他地方听到过一种说法:“现代 C++ 里应该使用异常。”但这之后呢?应该怎么去用异常呢?
所以,今天我就和你好好聊聊“异常那些事”,说一说为什么要有异常,该怎么用好异常,有哪些要注意的地方。

为什么要有异常?

很多人认为,C++ 里的“异常”非常可怕,一旦发生异常就是“了不得的大事”,这其实是因为没有理解异常的真正含义。
实际上,你可以按照它的字面意思,把它理解成“异于正常”,就是正常流程之外发生的一些特殊情况、严重错误。一旦遇到这样的错误,程序就会跳出正常流程,甚至很难继续执行下去。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《罗剑锋的 C++ 实战笔记》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(20)

  • 最新
  • 精选
  • eletarior
    初识编程时,对异常处理的误解还是挺大的,可能是因为异常处理总是在教材的最后几页,觉得很难,然后就草草的误解了。之前有一份工作领导特意提到了正确地进行异常处理。后来认真学了下,接触到C++11后,才真正用起来。 异常的好处是不言自明的,加强程序的健壮性,避免大量if else形式的代码处理。坏处也是有的,比如某个函数是否抛异常,写的人不好确定,要关注具体逻辑;而用这个函数的人也不确定,可能还需要借助注释来说明这个函数会抛出了怎样的异常。而抛异常就要try 和catch处理,不处理程序就会崩溃,接口的“客户”可能会不乐意处理。 用异常时,我更多的将某个函数声明为 noexcept ,比如构造函数,而明确要抛异常的函数则声明为noexcept(false)。 而在代码里处处使用 try catch也是不明智的,需要根据具体的场景和业务来辨别。而有一点是需要特别注意的避免在catch里写真实的业务代码,不应该在里写改变整个程序的流程的代码。如果程序崩溃了,就让它崩溃吧。

    作者回复: 非常好的经验,noexcept(false)这个显式声明对人很友好。

    47
  • Coder Wu
    个人感觉如果是封装功能库给其他人使用,可以考虑用异常,能方便传递错误信息给外部。如果是写业务逻辑的话,只是涉及到自身功能的错误,还是多用错误码的方式,并且配合日志,方便后续问题的跟踪。

    作者回复: 我的观点和你恰恰相反,库最好还是用错误码的方式,不要把异常抛给外面处理,通俗点说就是eat your own dog food。 因为外界不了解库的工作机制,也不知道怎么处理异常才好,或者可能根本就不知道你会抛异常,这样的话库用起来就不是太安全。

    4
    17
  • 范闲
    异常是个很重要的东西。 涉及磁盘操作的最好使用异常+调用栈, 涉及业务逻辑的最好利用日志+调用栈, 涉及指针和内存分配的还是用日志+调用栈吧,这种coredump一般是内存泄露和内存不够引起的。

    作者回复: 很不错的经验分享。

    4
    16
  • _smile滴水C
    老师我使用过python的try,能跳过数组越界错误,假设是用户代码,跳过并无大碍,避免直接崩溃,C++的try有跟python类似的用法吗?

    作者回复: 虽然都是try,但两者的用法差距很大,不能直接照搬Python的用法。 C++里的try就是捕获异常,而在C++里数组越界不一定会引发异常,可能正常也可能直接crash,try只能捕获可控的异常,不是万能的。

    5
  • 泰伦卢
    禁用异常典型的有google和美国国防部或者移动端,google是因为历史包袱,以前编译器对异常支持的不太好,所以都使用的错误码方式,近些年来沿用了之前得方式,不好两种错误处理方式都穿插到代码里,国防部是因为异常处理在catch异常时候程序运行速度受影响,移动端主要是因为异常处理的程序体积会大20-30%,而我们真的那么在意程序体积吗,我们普通人使用异常简直不能再香,只是用之前需要搞明白异常处理的各种注意事项

    作者回复: 说的很好,异常是个好东西,但用不好也会有负面影响,需要了解它的特性后再结合自己的实际情况。

    4
  • Stephen
    老师,"通过引入一个“中间层”来获得更多的可读性、安全性和灵活性"这句话中可读性和灵活性我可以理解,这是函数比较容易理解的特性,安全性怎么讲呢?请老师不惜赐教

    作者回复: 因为有了一个中间层,也就加上了一层额外的控制,可以在这里定制抛出异常的逻辑,避免外界随意使用异常,比如统一抛出std::exception,而不是int、string,所以就会安全一些。

    2
  • Eglinux
    class my_exception : public std::runtime_error { public: using this_type = my_exception; using super_type = std::runtime_error; public: my_exception(const char* msg): super_type(msg) {} my_exception() = default; ~my_exception() = default; private: int code = 0; }; 请问老师,这一句是什么语法?没看懂 my_exception(const char* msg): super_type(msg) {} 列表初始化吗?但是 super_type 不是成员变量呀

    作者回复: 这个就是调用父类的构造函数,只是被改名成super_type了。

    4
    2
  • xGdl
    两try-catch当函数体还是挺丑的吧

    作者回复: 我觉得function-try的形式更清晰,可以自己写写试试。

    2
  • reverse
    老师,在下说一下我在工作写代码三四年的时间内的一些感受 ,在下主要用的是nodejs java , node这块我觉得在es6之后可以结合promise async 函数 再配合try catch 写出简洁的的函数,java 要求的比较严格 ,它的编译器会强制要求你加上 try catch 尤其是对 io操作 来说 ,实际上node的io操作也需要如此,综上所述,偏向于业务逻辑的错误码需要自己合理的定义范围自己意义,涉及到硬件磁盘的需要强制性的捕获异常,设计大于编程,函数规格大于功能本身

    作者回复: 很有价值的经验,错误处理是写程序时很重要的一块,但有的时候会不太注重,异常可以强制我们去处理错误。

    2
  • Geek_6a1d96
    底层功能库采用错误码,业务逻辑部分采用观察者模型抛异常,谁注册成观察者谁处理异常。不管是log,显示在ui,或者用数据库记录异常,重启重连操作,把他们注册成观察者,就能很方便的跨类,跨dll完成异常处理。

    作者回复: great

    归属地:北京
    1
收起评论
显示
设置
留言
20
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部