06 | 异常:用还是不用,这是个问题
该思维导图由 AI 生成,仅供参考
没有异常的世界
- 深入了解
- 翻译
- 解释
- 总结
异常处理在C++中是一个备受关注的话题。本文首先介绍了异常处理的优势,指出使用异常能够简化代码结构,使得代码更加简洁和清晰。然后,文章讨论了一些大型项目禁用异常的原因,包括历史原因、性能问题以及对二进制文件大小和性能的追求。接着,文章提到了在特定项目中禁用异常可能是必要的,但对于一般项目来说,使用异常能够提高代码的可读性和可维护性。此外,文章还介绍了C++中异常的一些批评,包括异常违反了C++原则以及异常比较隐蔽的问题。最后,文章总结了使用异常的理由和不使用异常的理由,强调异常是C++中的标准错误处理方式,并提出了一些使用异常的建议。通过对比使用异常和不使用异常的代码示例,以及大型项目禁用异常的原因,全面地介绍了异常处理在软件开发中的重要性和实际应用情况。 总的来说,本文深入探讨了C++中异常处理的优势、禁用异常的原因以及使用异常的建议,为读者提供了全面的异常处理知识和实际应用情况,对于C++开发者来说具有很高的参考价值。
《现代 C++ 编程实战》,新⼈⾸单¥59
全部留言(32)
- 最新
- 精选
- tt文中下面的一句话: “首先是内存分配。如果 new 出错,按照 C++ 的规则,一般会得到异常 bad_alloc,对象的构造也就失败了。这种情况下,在 catch 捕捉到这个异常之前,所有的栈上对象会全部被析构,资源全部被自动清理。” 谈的是new在分配内存时的错误,是堆上内存的错误,但自动被析构的却是栈上的对象。一开始我想是不是笔误了,但仔细想想,堆上的东西都是由栈上的变量所引用的,栈上对象析构的过程,堆上相应的资源自然就被释放了。而且被释放的对象的范围还被栈帧限定了。
作者回复: 对,这就是 RAII,非常重要。 学习速度飞快啊。👍
2019-12-09537 - 亮看到老师说了部分开源的异常优秀的C++开源项目,老师能否推荐些现在流行的,能逐步深入的网络编程方面的C++开源项目看呢,从入门到深入的都推荐一些吧。谢谢老师
作者回复: 网络就看 Boost.Asio 吧。这个将是未来 C++ 网络标准库的基础。
2019-12-1729 - tokamak老师,你好。目前主流的开源项目中,有没有使用了异常的优秀的C++开源项目?可以用来作为参考案例。
作者回复: 我不觉得用异常有什么特别的地方,因而用异常的我个人没觉得有什么特别可参考的。 由于历史原因,有不少大名气的 C++ 程序没有使用异常,特别是 Google 的项目,比如 Chromium。不用异常,实际上是对用户友好(可执行文件略小,性能有可能有小提升),而对开发者更累。 我知道用到异常的一些项目: - Boost - C++ REST SDK - pytorch - pybind11 - Armadillo - nlohmann/json - cppcheck - OpenCV - LibreOffice 这篇文章也可以看一下: https://cppdepend.com/blog/?p=311
2019-12-1321 - fl260919784老师好,有没有编译器平台对异常的实现原理的资料呢,比如X86下G++ -S会发现安插了一些__cxa_throw之类的调用,感谢
作者回复: 可以参考下面这些链接: http://baiy.cn/doc/cpp/inside_exception.htm https://gcc.gnu.org/wiki/Dwarf2EHNewbiesHowto https://stackoverflow.com/questions/15670169/what-is-difference-between-sjlj-vs-dwarf-vs-seh
2020-02-176 - EncodedStar一、使用异常 1.异常处理并不意味着需要写显示的try和catch。异常安全的代码,可以没有任何try和catch 2.适当组织好代码,利用好RAII,实现矩阵的代码和使用矩阵的代码都可以更短、更清晰,处理异常一般情况会记日志或者向外界用户报告错误。 二、使用异常的理由 1.vector C++标准容器中提供了at成员函数,能够在下标不存在的时候抛出异常(out_of_range),作为一种额外的帮助调试手段 2.强异常保证,就是一旦异常发生,现场会恢复到调用异常之前的状态。(vector在元素类型没有提供保证不抛异常的移动构造函数的情况下,在移动元素时会使用拷贝构造函数,一旦某操作发生异常,就可以恢复原来的样子) 3.只要使用标准容器就都的处理可能引发的异常bad_alloc 4。可以使用异常,也可以使用assert 课后思考 你的C++项目里使用过异常吗?为什么? 答:按老师课里说的,只要使用了标准容器就得考虑使用处理异常(bad_alloc),所以,大部分C++代码如果保证安全的情况下都的考虑这个异常。当然也在别的地方,之前在读取配置文件(json文件)字段的时候加过,如果读取失败,异常抛出
作者回复: OK。很好!
2019-12-2225 - 怪兽异常真是一个大话题,请假老师2个疑问点: 1. 函数标不标noexcept有什么区别?标了noexcept,表示不会抛出异常,也就表示异常安全吗?异常安全的代码也就不需要try和catch了 。但实际上,不管函数有没有标noexcept,如果确实抛出了异常,就会调用std::terminate。所以总的来说,函数标了noexcept只是一种声明而已,是想告诉编译器它是安全的,可以被move或其他优化,老师这样理解对吗? 2. 看评论说,析构函数缺省就是noexcept,那么构造函数缺省也是noexcept的吗?有必要标noexcept吗?
作者回复: 1. 对。标 noexcept 是一种契约声明,表明该函数永远不应该抛异常,提供不抛异常保证,比强异常安全保证更强。 2. 只有析构函数默认有 noexcept 声明(前提是所有的基类和成员变量的析构函数都 noexcept)。构造函数函数如果不是 default 声明的话,仍需手工标 noexcept。
2021-06-094 - 中年男子用到异常的时候倒不是很多,但是异常千万别乱用,害人害己, 曾经同事离职,接手他项目的代码,把我坑的,几乎所有能引起crash的地方都用try catch 捕获异常,然而不处理异常,比如非法指针, 这种bug居然用try catch 来规避,坑了我两个月时间才把程序搞稳定了,现在想起他来,心里还有一句mmp想送给他。。。
作者回复: 任何东西用得不好都是坑。有朋友遇到小项目里用了一大堆(不必要的)设计模式,把代码硬生生弄得不可理解。不能说设计模式就是不好,是不? MSVC 可以用 ... 捕获非法指针操作,这也是极易被误用的功能。以前也遇到过一次,一不小心用了这个功能,把明明在调试时可以发现的崩溃变成了程序的怪异行为。不过,严格来讲这不属于 C++ 的异常……这实际上是 Windows 的 SEH,纯 C 里都能做得到。
2019-12-0934 - 林林为什么说“异常处理并不意味着需要写显式的 try 和 catch” 没有catch的话,程序不是会挂掉吗?
作者回复: 是的,没有catch,程序会挂掉。但你需要写出 try 和 catch 的地方很少。很多地方的异常处理,就是让程序优雅地通过异常退出当前函数。 就像我文中描述的“matrix c = a * b;”这句可能出异常的地方有好几处,目前的代码写法,也在某种意义上“处理”了异常——确保发生异常时程序行为的完全正常,即给出了异常安全保证。 又如异常安全的代码,常常是让会抛异常的操作最先做(如内存分配),然后再做其他不会抛异常的操作。这样的代码,一般不需要写 try... catch,也同样能在异常情况执行正确的流程。 这和 Java 这样的在编译期进行大量异常检查的语言不一样。在 Java 里,因为检查性异常(checked exception)的使用,你通常会需要写出多得多的 try...catch 语句。
2021-03-263 - 怪兽老师打扰一下,请教2点疑惑: 1. 原文中描述:“C++ 的标准容器在大部分情况下提供了强异常保证,即,一旦异常发生,现场会恢复到调用函数之前的状态,容器的内容不会发生改变,也没有任何资源泄漏。” 既然是强异常保证下发生异常,此时不就立即调用std::terminate结束程序了?还需要在意现场有没有恢复?容器的内容有没有发生变化? 2. 原文中描述:“只要你使用了标准容器,不管你自己用不用异常,你都得处理标准容器可能引发的异常——至少有 bad_alloc” 这应该是容器在分配内存时,并不是异常安全的,才会抛出的bad_alloc异常吧?如果分配内存是异常安全的,当发生异常抛出bad_alloc时,就立即std::terminate结束程序了,这样理解对吗?
作者回复: 1. 强异常安全保证不是无异常保证,只是说发生异常时保证还原现场,对象不发生变化。 2. 基本同上。你似乎把异常安全和noexcept保证混淆了。 异常安全有四级: 不抛异常(noexcept)保证 强异常安全保证 基本安全异常保证 没有任何保证
2021-09-182 - Minghao你好老师,学习了06异常和07迭代器,也自己写了一遍smart_pointer和istream_line_reader。您在课中提到了 “vector 会在元素类型没有提供保证不抛异常的移动构造函数的情况下,在移动元素时会使用拷贝构造函数“。我想请教一下,在自己开发的过程中,一般哪些成员函数需要考虑添加noexcept关键字呢?
作者回复: 最主要是移动构造函数、移动赋值函数和 swap。析构函数也不应该抛异常,但不用标,缺省就是 noexcept。其他函数,能确保没问题的也标一下,特别是很小的返回引用和指针的函数。
2020-05-272