ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

C++学习之异常机制详解

C++学习之异常机制详解 . 异常处理机制介绍C中的异常处理机制可以帮助我们处理程序在运行时可能会遇到的异常情况比如内存分配错误、文件打开失败等。当程序运行到某一处出现异常时程序会立即跳转到相应的异常处理代码。C中的异常处理使用try-catch语句实现try语句块中包含可能抛出异常的代码catch语句块用来捕获并处理异常。当程序执行到throw语句时就会抛出一个异常并跳转到最近的catch语句块处理异常。以下是一个简单的示例12345try{// 可能抛出异常的代码}catch(exception e) {// 处理异常}2. 如何抛出异常和捕获异常2.1 抛出异常在C中我们可以通过throw语句来抛出一个异常。throw后面跟着一个表达式它的类型可以是任意类型通常我们使用标准库中的异常类比如std::runtime_error、std::invalid_argument等。比如如果你抛出的int那后面你就需要在catch块中使用int类型来接收这个抛出的值以下是一个抛出异常的示例12345voidfoo(intx) {if(x 0) {throwstd::invalid_argument(x不能为负数);}}在上面的示例中如果参数x小于0就会抛出一个std::invalid_argument异常异常信息为x不能为负数。2.2 捕获异常当程序执行到throw语句时会跳转到最近的catch语句块处理异常。catch语句块中包含了捕获异常后要执行的代码。以下是一个捕获异常的示例12345try{foo(x);}catch(std::exception e) {// 处理异常}在上面的示例中如果foo函数抛出了一个std::exception异常就会跳转到catch语句块中进行处理。如果内部抛出的double则这里的std::exception就要写为double3. 如何实现自己的异常一般我们可以通过继承标准库中的异常类来实现自己的异常。通常情况下我们需要重写exception类中的what()方法以提供更详细的异常信息。以下是一个自定义异常的示例1234567891011121314151617classMyException :publicstd::exception {public:MyException(constchar* msg) : _msg(msg) {}virtualconstchar* what()constnoexcept override {return_msg.c_str();}private:std::string _msg;};voidfoo(intx) {if(x 0) {throwMyException(x不能为负数);}}在上面的示例中我们继承了std::exception类并重写了它的what()方法。然后在foo函数中如果参数x小于0就会抛出一个MyException异常异常信息为x不能为负数。4. 注意事项在使用异常处理时我们需要注意以下几点异常处理只是一种容错机制不能用来代替正常的程序代码逻辑。不要滥用异常处理应该只在必要的情况下使用。应该尽可能提供详细的异常信息以方便调试和定位问题。在捕获异常时应该考虑到可能发生的所有异常情况并分别进行处理。5. 面试常问的题目以下是一些常见的关于C异常处理的面试题目什么是C中的异常处理机制它的作用是什么如何抛出异常和捕获异常请给出一个示例。如果需要实现自己的异常应该怎么做请简述C中的异常类层次结构并说明它们的作用。在使用异常处理时有哪些需要注意的事项什么是异常安全性如何保证程序具有异常安全性请解释以下关键字的含义try、catch、throw、noexcept。如果一个函数可能抛出多种类型的异常应该如何进行捕获在C11中新增了一种异常处理机制即std::exception_ptr。请简述它的作用和使用方法。请介绍一下RAII技术在异常处理中的应用。以上是一些常见的面试题目希望能够对大家有所帮助。6. 答案什么是C中的异常处理机制它的作用是什么C中的异常处理机制是一种错误处理机制可以帮助我们处理程序在运行时可能会遇到的异常情况比如内存分配错误、文件打开失败等。当程序运行到某一处出现异常时程序会立即跳转到相应的异常处理代码。其主要作用在于在程序运行时发生异常后能够快速地定位并处理问题从而保证程序的稳定性和正确性。如何抛出异常和捕获异常请给出一个示例。我们可以通过throw语句来抛出一个异常catch语句块用来捕获并处理异常。以下是一个示例1234567891011121314voidfoo(intx) {if(x 0) {throwstd::out_of_range(x不能为负数);}}intmain() {try{foo(-1);}catch(std::exception e) {std::cout e.what() std::endl;}return0;}在上面的示例中如果参数x小于0就会抛出一个std::out_of_range异常异常信息为x不能为负数。在main函数中我们使用try-catch语句块来捕获异常并输出异常信息。如果需要实现自己的异常应该怎么做我们可以通过继承标准库中的exception类来实现自己的异常。通常情况下我们需要重写exception类中的what()方法以提供更详细的异常信息。以下是一个自定义异常的示例1234567891011121314151617classMyException :publicstd::exception {public:MyException(constchar* msg) : _msg(msg) {}virtualconstchar* what()constnoexcept override {return_msg.c_str();}private:std::string _msg;};voidfoo(intx) {if(x 0) {throwMyException(x不能为负数);}}在上面的示例中我们继承了std::exception类并重写了它的what()方法。然后在foo函数中如果参数x小于0就会抛出一个MyException异常异常信息为x不能为负数。请简述C中的异常类层次结构并说明它们的作用。C中的异常类层次结构如下所示std::exception所有标准异常类的基类包含了一些通用的异常信息。std::bad_alloc内存分配错误时抛出的异常。std::logic_error内部逻辑错误时抛出的异常例如无效参数或操作。std::runtime_error运行时错误时抛出的异常例如文件打开失败等。这些异常类都包含了一个what()方法返回一个描述异常信息的字符串。我们可以通过继承这些异常类来实现自己的异常。在使用异常处理时有哪些需要注意的事项在使用异常处理时我们需要注意以下几点异常处理只是一种容错机制不能用来代替正常的程序代码逻辑。不要滥用异常处理应该只在必要的情况下使用。应该尽可能提供详细的异常信息以方便调试和定位问题。在捕获异常时应该考虑到可能发生的所有异常情况并分别进行处理。什么是异常安全性如何保证程序具有异常安全性异常安全性是指程序在发生异常后能够正确地进行资源回收。保证程序具有异常安全性可以避免内存泄漏等问题。通常情况下我们可以通过RAIIResource Acquisition Is Initialization技术来保证程序具有异常安全性。RAII技术利用对象的生命周期来管理资源的分配和释放将资源的分配和释放过程封装在类的构造函数和析构函数中。例如我们可以使用std::vector来动态分配内存1234std::vectorint v;for(inti 0; i 10; i) {v.push_back(i);}当std::vector对象被销毁时它会自动调用析构函数来释放内存即使在循环中发生了异常也不会影响资源的释放。请解释以下关键字的含义try、catch、throw、noexcept。try用于包含可能抛出异常的代码块。catch用于捕获并处理异常的代码块。throw用于抛出一个异常并跳转到最近的catch语句块。noexcept指示一个函数不会抛出任何异常。如果一个函数可能抛出多种类型的异常应该如何进行捕获如果一个函数可能抛出多种类型的异常我们可以使用多个catch语句块来分别捕获这些异常。catch语句块的顺序应该从具体到一般以确保所有异常都能够被正确地捕获。以下是一个示例1234567891011121314151617181920voidfoo(intx) {if(x 0) {throwstd::invalid_argument(x不能为0);}elseif(x 0) {throwstd::out_of_range(x不能为负数);}}intmain() {try{foo(-1);}catch(std::invalid_argument e) {std::cout invalid argument: e.what() std::endl;}catch(std::out_of_range e) {std::cout out of range: e.what() std::endl;}catch(std::exception e) {std::cout exception: e.what() std::endl;}return0;}在上面的示例中如果foo函数抛出了一个std::invalid_argument异常就会跳转到第一个catch语句块进行处理如果抛出了一个std::out_of_range异常就会跳转到第二个catch语句块进行处理如果抛出了其他类型的异常就会跳转到最后一个catch语句块进行处理。在C11中新增了一种异常处理机制即std::exception_ptr。请简述它的作用和使用方法。std::exception_ptr是C11中新增的一种异常处理机制可以用来保存当前正在处理的异常并在稍后的时间点重新抛出该异常。以下是一个使用std::exception_ptr的示例123456789101112131415161718voidfoo() {try{// 可能会抛出异常的代码}catch(...) {std::exception_ptr p std::current_exception();// 处理异常std::rethrow_exception(p);}}intmain() {try{foo();}catch(std::exception e) {std::cout e.what() std::endl;}return0;}在上面的示例中如果foo函数抛出了异常就会跳转到catch语句块中处理异常并使用std::current_exception()函数获取当前正在处理的异常然后使用std::rethrow_exception()函数重新抛出该异常。在main函数中我们再次捕获这个异常并进行处理。请介绍一下RAII技术在异常处理中的应用。RAII技术在异常处理中的应用非常广泛。通过将资源的分配和释放过程封装在类的构造函数和析构函数中可以保证程序具有异常安全性。例如在操作文件时我们可以使用std::ofstream来打开文件并将其封装在一个类中1234567891011121314151617181920212223242526272829303132333435classFile {public:File(conststd::string filename) : _file(filename) {if(!_file.is_open()) {throwstd::runtime_error(failed to open file);}}~File() {if(_file.is_open()) {_file.close();}}voidwrite(conststd::string s) {_file s;}private:std::ofstream _file;};voidfoo() {File f(test.txt);f.write(hello, world);}intmain() {try{foo();}catch(std::exception e) {std::cout e.what() std::endl;}return0;}在上面的示例中我们定义了一个File类来封装文件操作构造函数中打开文件并检查是否成功打开析构函数中关闭文件。在foo函数中我们创建了一个File对象来进行文件写操作。无论在写入数据时是否发生异常File对象都会被正确地销毁并自动调用析构函数来关闭文件。这保证了程序具有异常安全性。总之RAII技术能够有效地提高代码的可靠性和可读性使得程序的异常处理更加简单和安全。7. 总结异常处理机制是C中非常重要的一个特性它可以帮助我们处理程序在运行时可能遇到的异常情况。在使用异常处理时我们需要注意抛出异常和捕获异常的方式并尽可能提供详细的异常信息。如果需要实现自己的异常可以通过继承标准库中的exception类来实现。同时我们也需要注意异常安全性保证程序在发生异常后能够正确地进行资源回收。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进