C/C++ - 异常处理
目录
错误处理
异常处理
异常传播
异常规划
标准异常
自定异常
错误处理
-
在C语言中,错误通常通过函数的返回值来表示。
-
错误返回值
- 对于能返回特殊值(如NULL或负值)的函数,在调用时检查这些值来处理错误。
-
#include <stdio.h> #include <stdlib.h> #define DIV_RET_SUCCESS 1 #define DIV_RET_FAILED 0 //C语言中通过函数返回值来判断执行成功与否 int myDiv(int num1, int num2, int* nRet) { if (num2 == 0) { *nRet - 0; return DIV_RET_FAILED; } *nRet = num1 / num2; return DIV_RET_SUCCESS; } int main() { int ret = 0; if (myDiv(10, 2, &ret) == DIV_RET_SUCCESS) { printf("%d \r\n", ret); } return 0; }
-
错误码全局变量
- 如 errno,这是一个全局变量,很多标准库函数在出错时会设置这个变量为相应的错误码。
-
#pragma warning(disable:4996) #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> int main() { FILE* pFile = NULL; pFile = fopen("0xCC.txt", "r"); if (pFile == NULL) { printf("ErrorCode -> %d \r\n", errno); printf("ErrorMesg -> %s \r\n", strerror(errno)); perror("fopen"); } return 0; }
-
宏定义
- 通过宏定义可以创建简单的错误处理代码块,这个方法提供了一种快速插入常用错误处理程序代码的方式。
-
#include <stdio.h> #include <stdlib.h> #define HANDLE_ERROR(msg) \ do { \ perror(msg); \ exit(EXIT_FAILURE); \ } while(0) int main() { FILE *fp = fopen("file.txt", "r"); if (!fp) { HANDLE_ERROR("Error opening file"); } // 其余代码... fclose(fp); return 0; }
异常处理
-
throw:当问题发生时,程序会抛出一个异常。这是通过 throw 关键字完成的,后面跟着要抛出的异常对象。
throw ErrorCode throw "Erroe"
-
try:try 块内的代码是可能产生异常的代码,当其中的代码抛出一个异常时,执行流会跳转到匹配的 catch 块。
-
catch:catch 块会捕获异常,并包含如何处理这些异常的代码。
try { //可能抛出异常的代码 } catch(ExceptionType var) //根据异常类型捕获 { //处理匹配异常类型 } catch(ExceptionType var) //根据异常类型捕获 { //处理匹配异常类型 }
-
示例代码
#include <iostream> int AllocMem() { //可能会抛出异常的代码放在try语句块内 try { //throw 'A'; long long* p = new long long[0xFFFFFFF]; //thorw bad_allocation } catch (int exception) { std::cout << exception << std::endl; } catch (char exception) { std::cout << exception << std::endl; } catch (std::exception exception) { std::cout << exception.what() << std::endl; } } int main() { AllocMem(); return 0; }
异常传播
-
C++异常传播(Exception Propagation)是指在程序中如果一个函数内发生了异常,而该异常没有在该函数内得到处理,则该异常会被传递到函数调用者处,如果调用者也不处理,则继续传递,这样一直到最顶层调用者。如果最顶层调用者也没有处理异常,则程序可能崩溃。
- 如果try块内的代码抛出了异常,控制流会跳到第一个匹配的catch块。
- 如果在当前函数中没有匹配的catch块,异常会被传递给调用该函数的函数,并在那里寻找匹配的catch块。
- 如果在任一函数中都找不到匹配的catch块,程序将调用terminate()结束程序。
#include <iostream> void Fun3() { throw 0xCC; } void Fun2() { try { Fun3(); } catch (char exception) { std::cout << exception << std::endl; } } void Fun1() { try { Fun2(); } catch (float exception) { std::cout << "Fun1 Exception" << std::endl; } } int main() { try { Fun1(); } catch (...) { std::cout << "Main Exception" << std::endl; } return 0; }
异常规划
- 异常规范(exception specification)是C++中的一种功能,它允许开发者指明一个函数可能抛出哪些异常。
- 自从C++11起,异常规范已经不再被推荐使用,取而代之的是noexcept关键字。
- void someFunction() throw(int, char); // 只能抛出int型和char型异常
- void someFunction() noexcept; // 不会抛出异常
-
#include <iostream> #include <vector> //该函数不会抛出任何异常 void Fun1() throw() { } void Fun2() noexcept { } void Fun3() throw(int) { } void Fun4() throw(char) { } void Fun5() throw(char, int) { } int main() { try { Fun3(); } catch (int exception) { std::cout << exception << std::endl; } try { Fun4(); } catch (char exception) { std::cout << exception << std::endl; } try { Fun5(); } catch (char exception) { std::cout << exception << std::endl; } catch (int exception) { std::cout << exception << std::endl; } return 0; }
标准异常
- std::exception 是各种标准异常类的基础,提供了一个异常层次结构。
-
std::logic_error
- 逻辑错误表示程序的逻辑不当导致的问题,这通常是可以预防的错误。
- std::domain_error: 当一个数学函数接收到一个不在其定义域内的参数时抛出。
- std::invalid_argument: 当传递给函数的参数无效时抛出。
- std::length_error: 当创建过大的 std::string 或者 std::vector 时抛出。
- std::out_of_range: 当通过 at 访问 std::vector 或 std::string 而下标超出范围时抛出。
-
std::runtime_error
- 运行时错误,表示在程序运行时发现的错误,通常是难以预防的。
- std::overflow_error: 当算术运算超过表示范围时抛出。
- std::underflow_error: 当算术运算结果在正确的表示范围内,但非正规化时抛出。
- std::range_error: 当计算的结果不在可表示的范围内时抛出。
- std::system_error: 用于报告系统错误的异常。
-
std::bad_alloc
当动态内存分配失败时(如 new 表达式),std::bad_alloc 被抛出。
-
std::bad_cast
使用动态转换 (dynamic_cast) 转换到不兼容的类型时,抛出 std::bad_cast。
-
std::bad_typeid
当使用类型信息功能(如 typeid 操作符)并且操作的对象是未定义类型时,抛出 std::bad_typeid 异常。
-
std::bad_function_call
当调用一个空的 std::function 对象时,抛出 std::bad_function_call 异常。
-
#include <iostream> int main() { try { long long* p = new long long[0xFFFFFFF]; } catch (const std::exception& e) { std::cout << e.what() << std::endl; } return 0; }
自定异常
-
#include <iostream> class myException :public std::exception { public: myException(std::string message): m_Message(message){} virtual const char* what() const { return m_Message.c_str(); } private: std::string m_Message; }; int main() { try { myException obj("cc exception"); throw obj; } catch (const std::exception& e) { std::cout << e.what() << std::endl; } return 0; }