C++中的错误处理方法(含示例代码)
在实际工作中,我们写的函数除了能完成要求的功能之外,常常需要对函数的输入进行检查、判断函数是否正确执行(比如有除0操作)、执行结果是否符合预期。如果运行出错,错误原因是什么?以及如何针对不同错误进行处理?
通常,我们有 3 种方式把错误信息传递给函数的调用者。1
方法1:通过函数返回值来告知调用者是否出错。
很多Windows的API就是通过返回值为0表示API调用成功,而返回值不为0表示在API调用的过程中出错了。微软为不同的非零返回值定义了不同的意义,调用者可以根据这些返回值判断出错的原因。
很多SDK的接口也会采用返回值的方式来传递错误信息,例如:有一个初始化的函数int32_t XXXSDK_Create( sdk_type_e nType, int32_t& nPDLLHandle )
,返回值如果非0,可以在SDK文档的错误码清单中找到对应错误原因。
void Init()
{int result = XXXSDK_Create(XXXSDK_CORE_SDK_TYPE,m_nPDLLHandle);if (result == 0){//成功,TODO……}else{//失败打印错误码,TODO……}
}
SDK文档中的错误码类似这样:
1005| XXXSDK_CORE_ERROR_INIT_FAIL| 初始化失败
1009| XXXSDK_CORE_ERROR_INVALID_PARAM| 无效的参数
1010| XXXSDK_CORE_ERROR_TIMEOUT| 操作超时
…………
这种方式最大的问题是使用不便,因为函数不能直接把计算结果通过返回值赋值给其他变量,同时也不能把这个函数计算的结果直接作为参数传递给其他函数。
方法2:设置一个全局变量保存错误信息。
此时我们可以在返回值中传递计算结果了。这种方法比第一种方法使用起来更加方便,因为调用者可以直接把返回值赋值给其他变量或者作为参数传递给其他函数。
Windows的很多API运行出错之后,也会设置一个全局变量。我们可以通过调用函数GetLastError()
分析这个表示错误的全局变量,从而得知出错的原因。
DWORD GetLastError(VOID)
获取调用线程的最后一个错误代码值。最后一个错误代码基于每个线程进行维护。多个线程不会覆盖彼此的最后错误代码。- 通过调用
SetLastError(DWORD dwErrCode)
函数来设置这个错误码。当函数的返回值指示此类调用将返回有用的数据时,应立即调用GetLastError
函数。这是因为某些函数在成功时会把最后一个错误码置为零。
FormatMessage与GetLastError结合使用,可以显示返回的错误消息。下面是一个示例:2
void ReportError()
{LPTSTR lpMessage;DWORD dwErrCode = GetLastError();FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |FORMAT_MESSAGE_FROM_SYSTEM,NULL, // no source buffer neededdwErrCode, // error code for this messageNULL, // default language ID(LPTSTR)&lpMessage, // allocated by fcnNULL, // minimum size of bufferNULL); // no insertsMessageBox(NULL, lpMessage, TEXT("File Error"), MB_ICONSTOP | MB_OK );LocalFree(lpMessage); // Free the memory allocated by FormatMessage
}
下面的示例包括一个错误处理功能,该功能可以打印错误消息并终止该进程。lpszFunction参数是设置的最后一个错误代码的函数的名称。3
#include <windows.h>
#include <strsafe.h>void ErrorExit(LPTSTR lpszFunction)
{ // Retrieve the system error message for the last-error codeLPVOID lpMsgBuf;LPVOID lpDisplayBuf;DWORD dw = GetLastError(); FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_IGNORE_INSERTS,NULL,dw,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),(LPTSTR) &lpMsgBuf,0, NULL );// Display the error message and exit the processlpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR)); StringCchPrintf((LPTSTR)lpDisplayBuf, LocalSize(lpDisplayBuf) / sizeof(TCHAR),TEXT("%s failed with error %d: %s"), lpszFunction, dw, lpMsgBuf); MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK); LocalFree(lpMsgBuf);LocalFree(lpDisplayBuf);ExitProcess(dw);
}void main()
{// Generate an errorif(!GetProcessId(NULL))ErrorExit(TEXT("GetProcessId"));
}
系统错误码是在WinError.h
中定义的,有时是由非系统软件返回的。错误码的描述一般不会非常具体,需要结合具体应用,仔细进行排查。
- windows系统错误码详见:system-error-codes
- 其他的windows错误处理方法见:error-handling-functions
但这个方式有个问题:调用者很容易就会忘记去检查全局变量,因此在调用出错的时候忘记做相应的错误处理,从而留下安全隐患。
方法3:异常捕获和处理。
当函数运行出错的时候,我们就抛出一个异常,我们还可以根据不同的出错原因定义不同的异常类型。因此函数的调用者根据异常的类型就能知道出错的原因,从而做相应的处理。
C++ 异常处理涉及到三个关键字:try、catch、throw。4
- throw: 当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的。
- catch: 在您想要处理问题的地方,通过异常处理程序捕获异常。catch 关键字用于捕获异常。
- try: try 块中的代码标识将被激活的特定异常。它后面通常跟着一个或多个 catch 块。
通过显式划分程序正常运行的代码块(try模块)和处理异常的代码块(catch模块),逻辑比较清晰。
除以零的异常处理代码示例:4
#include <iostream>
using namespace std;double division(int a, int b)
{if( b == 0 ){//抛出异常throw "Division by zero condition!";}return (a/b);
}int main ()
{int x = 50;int y = 0;double z = 0;try {//保护代码z = division(x, y);cout << z << endl;}catch (const char* msg) //捕获异常{//处理异常cerr << msg << endl;} return 0;
}
由于我们抛出了一个类型为 const char* 的异常,因此,当捕获该异常时,我们必须在 catch 块中使用 const char*。
小结
三种方式各有优劣。在自己封装SDK的时候,可以用返回值给出错误码;在处理系统错误码时,可以获取全局变量;在需要针对不同错误分类处理时,可以用try、catch、throw。
《剑指offer》 ↩︎
https://docs.microsoft.com/en-us/globalization/localizability/win32-formatmessage ↩︎
https://docs.microsoft.com/en-us/windows/win32/debug/retrieving-the-last-error-code ↩︎
https://www.runoob.com/cplusplus/cpp-exceptions-handling.html ↩︎ ↩︎
C++中的错误处理方法(含示例代码)相关推荐
- Java EasyExcel在Web网站中读写Excel的方法及示例代码
使用EasyExcel可以更容易简单在Web网站中读写Excel,本文主要介绍在Java Web网站中读写Excel方法及示例代码. 原文地址:Java EasyExcel在Web网站中读写Excel ...
- java设置excel标题栏_Java EasyExcel写入Excel中复杂头(head)表中的标题的方法及示例代码...
数据标题示例: 1、实体对象@Data public class ComplexHeadData { @ExcelProperty({"主标题", "字符串标题" ...
- Java 使用EasyExcel读取Excel中多个sheet方法及示例代码
本文主要介绍Java中,使用EasyExcel读取Excel文件中多个Sheet的方法,以及使用示例代码. 1.读取数据的实体对象 @Data public class DemoData {priva ...
- mysql去重函数的使用方法_MySQL中使用去重distinct方法的示例详解
一 distinct 含义:distinct用来查询不重复记录的条数,即distinct来返回不重复字段的条数(count(distinct id)),其原因是distinct只能返回他的目标字段,而 ...
- SpringMVC中404错误解决方法总结
SpringMVC中404错误解决方法总结 参考文章: (1)SpringMVC中404错误解决方法总结 (2)https://www.cnblogs.com/shangjun/p/6411353.h ...
- python代码大全和用法用量_Python生成器的使用方法和示例代码
本文是<Effect Python 编写高质量Python代码的59个有效方法>的学习笔记.主要记录生成器的使用方法和示例代码. 返回队列的函数 如果函数要产生一系列结果,那么最简单的做法 ...
- .NET(C#)调用Godex(科诚)条码打印机打条码的方法及示例代码
本文主要介绍.NET(C#)中,连接Godex(科诚)条码打印机,和打印条形码的方法,以及相关的示例代码. 原文地址:.NET(C#)调用Godex(科诚)条码打印机打条码的方法及示例代码
- Python pandas 保存Excel自动调整列宽的方法及示例代码
本文主要介绍Python中,使用pandas.ExcelWriter保存Excel文件数据时,自动判断调整列的宽度方法,以及相关的示例代码. 原文地址:Python pandas 保存Excel自动调 ...
- .NET Core(C#) EPPlus创建Excel(.xlsx)写入数据的方法及示例代码
EPPlus是一个使用Open Office XML(Xlsx)文件格式,能读写Excel(.xlsx)文件的开源组件.本文主要介绍.NET Core(C#)中使用EPPlus创建Excel(.xls ...
- python中延时函数_python中实现延时回调普通函数示例代码
python中实现延时回调普通函数示例代码 这篇文章主要给大家介绍了关于python中实现延时回调普通函数的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的 ...
最新文章
- pgpool-II的性能缺陷(二)
- AC日记——Red and Blue Balls codeforces 399b
- 在windows7上的通过composer安装yii2
- 读书印记 - 《异类:不一样的成功启示录》
- C语言之基本算法32—鞍点
- 实现连麦_直播课程系统如何实现互动连麦效果?
- 二叉树的先序、中序、后序遍历超详解
- Stanford CS230深度学习(五)CNN和ResNet
- Eclipse配置Tomcat并运行
- redhat7图形界面网卡设置_Redhat Linux Interprise基本网络配置与调试
- 如何处理Ibatis结合MySQL数据库使用时的事务操作
- tkinter 文本框 值改变_【动图详解】PPT中文本框的使用方法,初学者必看!
- 中国十大芯片企业排名
- 实验2014052801:动态网页中的动态思想
- java学习第八天内容
- 《Sony Vegas Pro 12标准教程》—— 2.6 添加背景音乐
- 聊聊CVE漏洞编号和正式公开那些事
- Fiddler抓包:详解Fiddler抓包工具软件使用教程
- 面试官:你连SSO都不懂,就别来面试了
- Linux配置拨号服务器
热门文章
- 【分享】纯js的n级联动列表框 —— 基于jQuery,支持下拉列表框和列表框,最重要的是n级,当然还有更重要的...
- 《大话设计模式》读书笔记-第13章 建造者模式
- vss团队开发工具使用(个人学习心得)
- WCF分布式安全开发实践(6):传输安全模式之自定义X509Certificate证书验证
- Hibernate多对多映射 - 连接表
- 解决MySQL 8.0 设置简单密码报错ERROR 1819 (HY000): Your password does not satisfy the current policy require...
- pipeline 发布war包
- 52个有效方法(1) - 了解Objective-C语言的起源
- 多用as少用强制类型转换
- 【C语言】数据结构C语言版 实验5 递归