目录
C语言与C++错误处置惩罚方式的对比及应用
一、C语言传统的错误处置惩罚方式
1. 终止步伐:assert
2. 返回错误码
缺点:
二、C++中的异常处置惩罚机制
1. 基本概念
2. 异常的抛出与捕获
3. 异常的重新抛出
三、C++中的异常安全
1. 构造函数与析构函数的异常
2. RAII(资源获取即初始化)
3.利用
3.异常的利用
四、C++异常体系的优缺点
优点:
缺点:
五、实际应用
1.自界说异常体系
⭕1. Exception 基类
2. SqlException 子类
3. CacheException 子类
4. HttpServerException 子类
5. 模拟抛出异常的函数
SQLMgr()
CacheMgr()
HttpServer()
6. 异常处置惩罚的主函数 main()
7. 步伐举动
⭕ 总结
2.C++尺度库的异常体系
六. 总结
C语言与C++错误处置惩罚方式的对比及应用
在编程中,错误处置惩罚是不可制止的。传统的C语言和现代的C++在处置惩罚错误上有着显着的区别,前者依赖返回错误码的方式,而后者则引入了更为灵活的异常机制。这篇文章将探讨这两种语言在错误处置惩罚方面的不同,并介绍怎样在实际工程中公道利用它们。
一、C语言传统的错误处置惩罚方式
1. 终止步伐:assert
C语言中最简单的错误处置惩罚方式之一是直接终止步伐,例如利用 assert。当步伐在运行时遇到不可恢复的错误(如除零、内存访问越界等),步伐会被强制终止。这种方式虽然简单,但有显着的缺陷:
- 用户体验不佳:一旦步伐遇到题目就立即瓦解,用户无法继续利用步伐,特殊是在遇到小题目时。
- 缺乏灵活性:步伐员无法提供其他的错误处置惩罚路径,例如记载日志、尝试恢复等。
2. 返回错误码
另一种常用的处置惩罚方式是返回错误码。很多C语言的库都会通过返回一个整数值来表现函数的执行效果。具体的错误信息通常会被存储在全局变量 errno 中,步伐员可以通过查阅 errno 的值来判断堕落的具体原因。
示例代码:
- int func() {
- if (/* 错误发生 */) {
- errno = EINVAL; // 设置错误码
- return -1; // 返回错误
- }
- return 0; // 返回成功
- }
复制代码 缺点:
- 额外的错误处置惩罚负担:步伐员需要手动查抄每个函数调用的返回值,并根据 errno 做出相应的处置惩罚。这增加了代码的复杂性和堕落的可能性。
- 函数调用链复杂:如果错误在深层函数中发生,那么需要逐层返回错误,直到外层函数捕捉并处置惩罚,这导致代码难以维护。
例如:
- 下面这段伪代码我们可以看到ConnnectSql中堕落了,先返回给ServerStart,ServerStart再返回给main函数,main函数再针对题目处置惩罚具体的错误。
- 如果是异常体系,不管是ConnnectSql照旧ServerStart及调用函数堕落,都不用查抄,由于抛出的异常异常会直接跳到main函数中catch捕获的地方,main函数直接处置惩罚错误。
- int ConnnectSql()
- {
- // 用户名密码错误
- if (...)
- return 1;
- // 权限不足
- if (...)
- return 2;
- }
- int ServerStart() {
- if (int ret = ConnnectSql() < 0)
- return ret;
- int fd = socket()
- if(fd < 0)
- return errno;
- }
- int main()
- {
- if (ServerStart() < 0)
- ...
- return 0;
- }
复制代码 二、C++中的异常处置惩罚机制
C++ 引入了异常处置惩罚机制,使得步伐在遇到错误时不需要直接终止或通过返回值处置惩罚,可以通过抛出异常的方式将题目传递到合适的地方进行处置惩罚。
1. 基本概念
- throw:用于在错误发生时抛出异常。异常可以是任何类型的对象,步伐员可以抛出字符串、整型或自界说对象来传递错误信息。
- try:包含可能抛出异常的代码块。如果 try 块中的代码抛出了异常,步伐会跳转到相应的 catch 块处置惩罚异常。
- catch:用于捕获异常。catch 块可以捕获特定类型的异常,并根据异常类型进行处置惩罚。
示例代码:
- double Division(int a, int b) {
- if (b == 0)
- throw "Division by zero condition!"; // 抛出异常
- return (double)a / (double)b;
- }
- int main() {
- try {
- cout << Division(10, 0) << endl; // 可能抛出异常
- } catch (const char* errmsg) {
- cout << errmsg << endl; // 捕获并处理异常
- }
- return 0;
- }
复制代码
2. 异常的抛出与捕获
C++的异常处置惩罚机制基于类型匹配。在抛出异常时,步伐会根据异常的类型查找最接近的 catch 块。如果没有匹配的 catch,步伐将继续沿调用链向外查找,最终未被捕获的异常会导致步伐终止。
- 类型匹配:抛出的异常必须与 catch 块的类型匹配,否则将无法捕获。
- 继续与多态:可以抛出派生类对象,并利用基类捕获。这在复杂项目中非常实用,由于可以通过捕获基类来处置惩罚一类相干的错误。
- catch (...) {
- // 捕获所有类型的异常,防止程序崩溃
- cout << "Unknown exception occurred!" << endl;
- }
复制代码
3. 异常的重新抛出
有时,捕获到异常后,当前函数无法处置惩罚该异常,而需要将其传递给更高层的函数来处置惩罚。这时,可以通过 throw 关键字重新抛出异常。
- catch (...) {
- // 做一些处理,例如释放资源
- throw; // 重新抛出异常
- }
复制代码 三、C++中的异常安全
异常处置惩罚虽然强大,但在C++中引入了新的风险,特殊是资源泄漏题目。异常抛出后,如果没有正确释放资源(如内存、文件句柄等),可能导致步伐资源泄漏。
1. 构造函数与析构函数的异常
- 构造函数:如果构造函数抛出异常,可能导致对象没有被完全构造,进而出现内存泄漏等题目。
- 析构函数:最好不要在析构函数中抛出异常,析构函数的主要职责是清算资源,如果抛出异常,可能导致资源无法被正确释放。
2. RAII(资源获取即初始化)
C++利用RAII技术来确保资源在异常发生时也能被正确释放。通过智能指针等工具,步伐员可以确保资源在超出作用域时自动被释放,制止了内存泄漏题目。
- std::unique_ptr<int[]> array(new int[10]);
复制代码 3.利用
3.异常的利用
我们先看看异常怎么用的,再说其他细节!
下面这段代码出现除0错误步伐就会终止。但我不想让步伐终止,因此当被除数为0就抛一个异常,很简单就是用throw背面加一个对象,可以是任意类型对象如int、string等等。背面catch对异常进行捕获。
- double Division(int a, int b)
- {
- // 当b == 0时抛出异常
- if (b == 0)
- throw "Division by zero condition!";
- else
- return ((double)a / (double)b);
- }
- void Func()
- {
- int len, time;
- cin >> len >> time;
- cout << Division(len, time) << endl;
- }
- int main()
- {
- try {
- Func();
- }
- catch (const char* errmsg)
- {
- cout << errmsg << endl;
- }
- return 0;
- }
复制代码 测试:
逐语句调试,感受一下捕获的过程:
四、C++异常体系的优缺点
优点:
- 清晰的错误信息:通过异常对象,步伐员可以具体描述错误信息,便于调试。
- 简化代码逻辑:相比返回错误码的方式,异常处置惩罚可以自动沿调用链流传错误,镌汰显式的错误处置惩罚逻辑。
- 支持多态:通过抛出派生类异常,捕获基类异常,实现同一的异常处置惩罚。
缺点:
- 步伐执行流混乱:异常使得步伐的执行流不再线性,导致调试困难。
- 性能开销:尽管现代硬件的处置惩罚速度很快,但异常的捕获与栈睁开仍有一定的性能影响。
- 容易导致资源泄漏:没有正确处置惩罚异常时,资源(如内存、文件句柄)可能无法被释放,导致泄漏。
五、实际应用
1.自界说异常体系
- 实际利用中很多公司都会自界说本身的异常体系进行规范的异常管理,由于一个项目中如果大家随意抛异常,那么外层的调用者基本就没办法玩了
- 以是实际中都会界说一套继续的规范体系。 这样大家抛出的都是继续的派生类对象,捕获一个基类就可以了
下面代码展示了一个服务器开发中常用的异常继续体系,并模拟了数据库、缓存、HTTP服务器中的错误处置惩罚方式。通过继续 Exception 类,界说了不同类型的异常类,模拟了抛出和捕获异常的过程。让我们渐渐解释代码中的各个部分。
⭕1. Exception 基类
- class Exception
- {
- public:
- Exception(const string &errmsg, int id)
- : _errmsg(errmsg), _id(id)
- {}
- virtual string what() const//捕获后的处理
- {
- return _errmsg;
- }
- protected:
- string _errmsg; // 错误信息
- int _id; // 错误编号
- };
复制代码
- Exception 是一个基类,代表通用的异常。它有两个成员变量:
- _errmsg:表现错误信息。
- _id:表现错误的编号(可以用作错误分类或错误码)。
- what() 函数是一个虚函数,用于返回错误信息。子类可以重写这个方法,提供更多的上下文信息。
2. SqlException 子类
- class SqlException : public Exception
- {
- public:
- SqlException(const string &errmsg, int id, const string &sql)
- : Exception(errmsg, id), _sql(sql)
- {}
- virtual string what() const
- {
- string str = "SqlException:";
- str += _errmsg;
- str += "->";
- str += _sql;
- return str;
- }
- private:
- const string _sql; // SQL查询语句
- };
复制代码
- SqlException 是 Exception 的派生类,专门用于处置惩罚数据库相干的异常。
- 它有一个额外的成员变量 _sql,用于存储触发异常的 SQL 查询语句。
- 它重写了 what() 方法,提供了更具体的错误信息,包含了 SQL 查询语句,用于调试和定位题目。
3. CacheException 子类
- class CacheException : public Exception
- {
- public:
- CacheException(const string &errmsg, int id)
- : Exception(errmsg, id)
- {}
- virtual string what() const
- {
- string str = "CacheException:";
- str += _errmsg;
- return str;
- }
- };
复制代码
- CacheException 也是从 Exception 继续而来,表现缓存相干的异常。
- 它没有额外的成员变量,只重写了 what() 方法,返回了缓存错误的标识 CacheException: 和相应的错误信息。
4. HttpServerException 子类
- class HttpServerException : public Exception
- {
- public:
- HttpServerException(const string &errmsg, int id, const string &type)
- : Exception(errmsg, id), _type(type)
- {}
-
- virtual string what() const
- {
- string str = "HttpServerException:";
- str += _type;
- str += ":";
- str += _errmsg;
- return str;
- }
- private:
- const string _type; // HTTP请求类型 (如 "GET", "POST")
- };
复制代码
- HttpServerException 是另一个从 Exception 派生的类,处置惩罚 HTTP 服务器相干的异常。
- 它多了一个 _type 成员变量,表现 HTTP 请求的类型(如 GET 或 POST)。
- 它同样重写了 what() 方法,返回更具体的 HTTP 相干错误信息。
5. 模拟抛出异常的函数
SQLMgr()
- void SQLMgr()
- {
- srand(time(0));
- if (rand() % 7 == 0)
- {
- throw SqlException("权限不足", 100, "select * from name = '张三'");
- }
- }
复制代码
- SQLMgr 模拟了数据库操纵,在某些情况下会抛出 SqlException,表现数据库权限不足的异常,并附带了 SQL 查询语句。
CacheMgr()
- void CacheMgr()
- {
- srand(time(0));
- if (rand() % 5 == 0)
- {
- throw CacheException("权限不足", 100);
- }
- else if (rand() % 6 == 0)
- {
- throw CacheException("数据不存在", 101);
- }
- SQLMgr();
- }
复制代码
- CacheMgr 模拟了缓存管理操纵,它可能会抛出两种不同的 CacheException:权限不足或数据不存在。
- 如果缓存没有抛出异常,它还会调用 SQLMgr,这可能会进一步抛出 SqlException。
HttpServer()
- void HttpServer()
- {
- srand(time(0));
- if (rand() % 3 == 0)
- {
- throw HttpServerException("请求资源不存在", 100, "get");
- }
- else if (rand() % 4 == 0)
- {
- throw HttpServerException("权限不足", 101, "post");
- }
- CacheMgr();
- }
复制代码
- HttpServer 模拟了 HTTP 请求处置惩罚,可能抛出 HttpServerException,表现请求资源不存在或权限不足。
- 如果没有发生 HTTP 异常,它会调用 CacheMgr,因此可能抛出缓存或数据库相干的异常。
6. 异常处置惩罚的主函数 main()
- int main()
- {
- while (1)
- {
- try
- {
- HttpServer(); // 可能抛出多种异常
- }
- catch (const Exception& e) // 捕获基类的异常
- {
- cout << e.what() << endl; // 多态调用,输出具体的异常信息
- }
- catch (...)
- {
- cout << "Unkown Exception" << endl; // 捕获所有未明确处理的异常
- }
- }
- return 0;
- }
复制代码
- 在 main() 函数中,步伐通过 try-catch 块捕获全部从 HttpServer() 抛出的异常。
- 利用基类 Exception 的引用捕获全部派生类异常,并通过多态机制调用派生类的 what() 方法输出具体的错误信息。
- catch(...) 捕获全部没有明白类型的异常,确保即使抛出了未知类型的异常,步伐也不会瓦解。
7. 步伐举动
在每次循环中,步伐随机抛出不同的异常,如:
- SQL权限不足 (SqlException)
- 缓存数据不存在 (CacheException)
- HTTP请求资源不存在 (HttpServerException)
这些异常会被捕获,并根据异常类型输出相应的错误信息。步伐不会由于异常而瓦解,由于有全面的异常捕获机制。
运行:
会随机捕获异常~
⭕ 总结
- 继续与多态:派生类如 SqlException、CacheException、HttpServerException 通过继续 Exception 基类实现了多态。即在捕获基类异常时,能够正确辨认并调用派生类的 what() 方法。
- 异常处置惩罚结构化:通过不同的异常类型,开发者可以根据题目的种别和严重程度灵活处置惩罚不同模块的错误,好比数据库、缓存和HTTP服务器。
- 防御性编程:通过 catch(...) 捕获未辨认的异常,确保步伐不会因未捕获的异常导致瓦解,从而提高了步伐的稳定性。
这是一个常见的异常处置惩罚体系,在服务器开发和大型系统中尤为重要。
2.C++尺度库的异常体系
C++提供了一系列尺度的异常,我们可以在步伐中利用这些尺度的异常。它们是以父子类条理结构组织起来的
说明:
- 实际中我们可以去继续exception类实现本身的异常类
- 但是实际中很多公司像上面一 样本身界说一套异常继续体系。由于C++尺度库计划的不够好用
六. 总结
无论是C语言的返回错误码照旧C++的异常机制,错误处置惩罚都是步伐开发中的重要组成部分。C语言适合处置惩罚简单错误,而C++的异常处置惩罚则为复杂项目提供了更多的灵活性。公道利用这两种语言的错误处置惩罚方式,可以提高步伐的结实性和可维护性。
- C语言中,优先利用返回错误码:对于简单的错误,C语言通过返回值来处置惩罚是公道的。需要留意查抄每个函数调用的返回值,确保实时处置惩罚错误。
- C++中,建议利用异常处置惩罚:对于复杂的应用,C++的异常机制更为合适,尤其是在处置惩罚构造函数等无法返回错误码的场景下。
- 遵循异常安全原则:在异常可能引发的资源管理题目上,利用RAII(如智能指针)来确保资源的正确释放。
- 自界说异常体系:在大型项目中,建议自界说异常类,并通过继续实现不同模块的异常管理。捕获基类异常可以简化代码,并提高异常处置惩罚的灵活性。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |