您的位置:首页 > 游戏 > 手游 > 哪里可以找到免费的源码_典型b2c模式的网站_企业网站模板免费_网站发稿平台

哪里可以找到免费的源码_典型b2c模式的网站_企业网站模板免费_网站发稿平台

2025/5/13 5:18:55 来源:https://blog.csdn.net/2302_79376097/article/details/146074388  浏览:    关键词:哪里可以找到免费的源码_典型b2c模式的网站_企业网站模板免费_网站发稿平台
哪里可以找到免费的源码_典型b2c模式的网站_企业网站模板免费_网站发稿平台

前言

在 C++ 编程中,异常处理是一种强大的机制,能够帮助程序在运行时优雅地处理错误,避免崩溃,提高程序的健壮性和可维护性。相比 C 语言通过错误码进行错误处理的方式,C++ 的异常处理提供了更清晰、更结构化的错误处理方法。本文将详细介绍 C++ 异常的概念、异常的抛出与捕获、栈展开、异常匹配、异常的重新抛出、异常安全以及异常规范等内容。


1. 异常的概念

在 C++ 中,异常(Exception)是一种特殊的事件,当程序运行过程中发生错误时,程序可以抛出异常,并由相应的异常处理代码进行处理。

异常处理的核心思想

异常处理的核心思想是将问题的检测与问题的解决分开

  • 检测错误的代码 负责发现问题,并抛出异常。
  • 异常处理代码 负责捕获异常并执行相应的错误处理逻辑。

C 语言与 C++ 异常的区别

在 C 语言中,通常使用 错误码 进行错误处理:

  • 需要手动检查函数的返回值,以判断是否出现错误。
  • 错误码需要进行分类编号,程序需要额外的错误查询机制,处理逻辑复杂。

C++ 通过异常对象进行错误处理:

  • 直接抛出一个异常对象,其中可以包含完整的错误信息。
  • 由异常处理机制自动捕获异常,避免手动检查返回值,使代码更清晰。 

2. 异常的抛出与捕获

当程序检测到错误时,可以使用 throw 关键字抛出异常对象,并使用 catch 语句捕获异常进行处理。

异常的处理流程

  1. 发生错误时,程序会 throw 一个异常对象。
  2. 异常会沿着调用链向上传播,直到找到匹配的 catch 语句。
  3. 如果找到匹配的 catch,则执行其中的异常处理代码。
  4. 如果没有找到匹配的 catch,则程序会终止。

抛出异常的规则

  • throw 语句后面的代码不会被执行,程序会立即跳转到匹配的 catch 语句。
  • 异常对象会被复制一份,并在 catch 语句处理后销毁。
  • catch 语句必须与异常的类型匹配,否则异常会继续向上传播。

匹配规则

  • 严格匹配catch 语句的类型必须与 throw 语句的类型一致,或者是它的基类。
  • 基类匹配:如果 catch 语句的参数是基类对象,可以捕获派生类异常(多态)。
  • 通配符捕获:使用 catch (...) 语句,可以捕获所有类型的异常。

throw 语句后面的代码不会被执行,程序会立即跳转到匹配的 catch 语句。根据错误类型,会跳到main函数的catch语句。

#include <iostream>
#include <vector>
using namespace std;
double divide(int a, int b) {try {if (b == 0) {string error = "Divide by zero";throw error;}elsereturn (double)a / b;}catch (int errid) {cout << errid << endl;}return 0;
}
void Func(){int len, time;cin >> len >> time;try{cout << divide(len, time) << endl;}catch (const char* errmsg){cout << errmsg << endl;}cout << __FUNCTION__ << ":" << __LINE__ << "行执行" << endl;
}
int main() {while (1) {try {Func();}catch (const string& errmsg) {cout << errmsg << endl;}}return 0;
}

下面j就是调用最近的Func函数的catch语句 

double divide(int a, int b) {if (b == 0) {throw string("Divide by zero"); // 直接抛出 std::string}return (double)a / b;
}void Func() {int len, time;cin >> len >> time;try {cout << divide(len, time) << endl;}catch (const string& errmsg) { cout << "Error in Func: " << errmsg << endl;}cout << __FUNCTION__ << ":" << __LINE__ << " 行执行" << endl;
}int main() {while (true) {try {Func();}catch (const string& errmsg) { cout << "Error in main: " << errmsg << endl;}}return 0;
}


3. 栈展开

栈展开(Stack Unwinding)指的是当异常发生时,程序会沿着函数调用栈回溯,查找匹配的 catch 语句的过程。

栈展开的过程

  1. 检查当前函数 是否有匹配的 catch 语句:
    • 如果有,跳转到 catch 语句处理异常。
    • 如果没有,退出当前函数,并在上层调用链中继续查找。
  2. 清理局部变量
    • 退出函数时,会调用当前作用域内的局部对象的析构函数,释放资源。
  3. 继续向上查找 catch
    • 如果找到了匹配的 catch,程序继续执行 catch 语句中的代码。
    • 如果一直找不到 catch,最终会到达 main 函数。
  4. 程序终止
    • 如果 main 函数也没有捕获异常,程序会调用 std::terminate() 终止执行。


4. 异常的匹配规则

当异常被抛出时,程序会寻找匹配的 catch 语句进行处理,匹配规则如下:

  • 完全匹配:抛出的异常类型和 catch 语句的参数类型完全一致。
  • 常量转换:允许从非常量常量转换(如 intconst int)。
  • 数组和指针转换:允许数组转换成指向数组元素类型的指针。
  • 派生类向基类转换:允许派生类异常被基类的 catch 语句捕获(多态)。
  • 通配符匹配catch (...) 可捕获所有异常,但无法识别异常类型。
#include<thread>// ⼀般⼤型项⽬程序才会使⽤异常,下⾯我们模拟设计⼀个服务的⼏个模块
// 每个模块的继承都是Exception的派⽣类,每个模块可以添加⾃⼰的数据
// 最后捕获时,我们捕获基类就可以
class Exception
{
public:Exception(const string& errmsg, int id):_errmsg(errmsg), _id(id){}virtual string what() const{return _errmsg;}int getid() const{return _id;}
protected:string _errmsg;int _id;
};
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;
};
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;}
};
class HttpException : public Exception
{
public:HttpException(const string& errmsg, int id, const string& type):Exception(errmsg, id), _type(type){}virtual string what() const{string str = "HttpException:";str += _type;str += ":";str += _errmsg;return str;}
private:const string _type;
};
void SQLMgr()
{if (rand() % 7 == 0){throw SqlException("权限不足", 100, "select * from name = '张三'");}else{cout << "SQLMgr 调用成功" << endl;}
}void CacheMgr()
{if (rand() % 5 == 0){throw CacheException("权限不足", 100);}else if (rand() % 6 == 0){throw CacheException("权限不存在", 101);}else{cout << "CacheMgr 调用成功" << endl;}SQLMgr();
}
void HttpServer()
{if (rand() % 3 == 0){throw HttpException("请求资源不存在", 100, "get");}else if (rand() % 4 == 0){throw HttpException("权限不足", 101, "post");}else{cout << "HttpServer调用成功" << endl;}CacheMgr();
}
int main()
{srand(time(0));while (1){this_thread::sleep_for(chrono::seconds(1));try{HttpServer();}catch (const Exception& e) // 这⾥捕获基类,基类对象和派⽣类对象都可以被捕获{cout << e.what() << endl;}catch (...){cout << "Unkown Exception" << endl;}}return 0;
}


5. 异常的重新抛出

在某些情况下,一个 catch 语句可能需要部分处理异常,然后重新抛出它,让更外层的 catch 处理剩余部分。可以使用 throw; 重新抛出当前异常。

典型应用

  • 先记录错误日志,然后重新抛出异常,让外层代码进行更详细的处理。
#include <iostream>
using namespace std;void func() {int* arr = new int[10]; // 申请内存try {throw runtime_error("Memory error"); // 模拟异常}catch (const exception& e) {cout << "Error in func: " << e.what() << endl;delete[] arr; // 释放资源throw; // 重新抛出异常}
}int main() {try {func();}catch (const exception& e) {cout << "Main caught: " << e.what() << endl;}return 0;
}


6. 异常安全问题

异常处理的一个关键问题是如何确保资源不会因为异常而泄漏,例如:

  • 动态内存分配:如果在 new 之后异常被抛出,delete 可能不会被执行,导致内存泄漏。
  • 锁管理:如果在获取锁后抛出异常,而没有释放锁,则可能导致死锁。
  • 文件操作:打开文件后,异常可能导致文件未能正确关闭。

解决方案

  1. RAII(资源获取即初始化)

    • 资源在构造函数中分配,在析构函数中释放,确保异常安全。
    • 例如使用 智能指针(std::unique_ptrstd::shared_ptr 来管理内存。
  2. try-catch 处理资源释放

    • catch 语句中手动释放资源,然后重新抛出异常。
  3. 避免异常逃离析构函数

    • 析构函数不应抛出异常,否则会导致程序 std::terminate() 终止。
double Divide(int a, int b)
{// 当b == 0时抛出异常if (b == 0){throw "Division by zero condition!";}return (double)a / (double)b;
}
void Func()
{// 这⾥可以看到如果发⽣除0错误抛出异常,另外下⾯的array没有得到释放。// 所以这⾥捕获异常后并不处理异常,异常还是交给外层处理,这⾥捕获了再重新抛出去。int* array = new int[10];try{int len, time;cin >> len >> time;cout << Divide(len, time) << endl;}catch (...){// 捕获异常释放内存cout << "delete []" << array << endl;delete[] array;throw; // 异常重新抛出,捕获到什么抛出什么}cout << "delete []" << array << endl;delete[] array;
}
int main()
{try{Func();}catch (const char* errmsg){cout << errmsg << endl;}catch (const exception& e){cout << e.what() << endl;}catch (...){cout << "Unkown Exception" << endl;}return 0;
}


7. 异常规范

C++ 提供了一些异常规范,用于声明函数是否会抛出异常。

C++98 的异常规范

  • throw():表示函数不会抛出任何异常。
  • throw(int, char, std::exception):表示函数可能抛出指定类型的异常。

问题:C++98 的异常规范很复杂,难以维护,并且编译器不会强制检查。

C++11 的 noexcept

  • noexcept 关键字替代了 throw(),表示函数不会抛出异常:
    void func() noexcept;
    
  • noexcept(expression):可以检测表达式是否可能抛出异常:
    bool canThrow = noexcept(func());
    
  • 如果 noexcept 修饰的函数抛出异常,程序会终止!

实践建议

  • 仅在确保函数不会抛出异常时使用 noexcept,否则可能导致程序异常终止。

总结

C++ 的异常处理机制提供了一种清晰、结构化的错误处理方式,避免了 C 语言繁琐的错误码处理。关键点如下:

  1. throw 抛出异常,catch 捕获异常,异常对象可以携带详细错误信息。
  2. 异常沿着调用链传播(栈展开),直到找到匹配的 catch,否则程序终止。
  3. 异常安全 是编写健壮代码的重要原则,应使用 RAII、智能指针 等机制避免资源泄漏。
  4. C++11 noexcept 规范化了异常处理,提高了代码的可维护性和优化能力。

掌握 C++ 异常处理,将有助于编写更加稳定和健壮的程序! 🚀

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com