做网站 收费,网站上传文件功能实现,chn域名注册网站,公司官网首页设计我不怕练过一万种腿法的对手,就怕将一种腿法 练一万次的对手。
什么是C的异常 在C中#xff0c;异常处理通常使用try-catch块来实现。try块用于包含可能会抛出异常的代码#xff0c;而catch块用于捕获并处理异常。当异常被抛出时#xff0c;程序会跳过try块中未执行… 我不怕练过一万种腿法的对手,就怕将一种腿法 练一万次的对手。
什么是C的异常 在C中异常处理通常使用try-catch块来实现。try块用于包含可能会抛出异常的代码而catch块用于捕获并处理异常。当异常被抛出时程序会跳过try块中未执行的代码并在catch块中执行适当的处理操作。如果没有抛出异常则catch块将被跳过。 以下是一个简单的C程序演示了如何使用异常处理 #include iostreamint main() {try { // 尝试执行下面的代码块如果发生异常则跳转到catch块处理异常int x 10; int y 0; if (y 0) { // 如果y等于0抛出一个异常throw Division by zero!; // 抛出一个字符串类型的异常对象内容为Division by zero!}int result x / y; std::cout Result: result std::endl; }catch (const char* error) { // 捕获一个字符串类型的异常对象将异常对象赋值给变量errorstd::cerr Error: error std::endl; // 输出错误消息内容为Error: Division by zero!}return 0;
}/*
在上面的程序中try块包含了可能会抛出异常的代码
包括将变量y赋值为0和使用除法运算符计算x除以y的结果。
如果y等于0程序会抛出一个异常内容为Division by zero!。
然后catch块用于捕获并处理该异常输出错误消息Error: Division by zero!。
*/ 异常对象 异常对象是一种特殊的对象。编译器依据异常抛出表达式构造异常对象即异常对象总是被拷贝。对象的类型是由表达式所表示对象的静态编译类型决定的。如Parent rObj Child; throw rObj;时会抛出Parent类型的异常对象。 异常对象存放在内存特殊位置该位置既不是栈也不是堆在Windows中是放在线程信息TIB中。该对象由异常机制负责创建和释放g和vc下存储区域处理略有差异。 异常对象不同于函数的局部对象局部对象在函数调用结束后就被自动销毁而异常对象将驻留在所有可能激活的catch语句都能访问到的内存空间中。当异常对象与catch语句成功匹配后在该catch语句的结束处被自动析构。 在函数中返回局部变量的指针或引用几乎肯定会造成错误。同理在throw语句中抛出局部变量的指针或引用也几乎是错误的。 // 捕获异常对象 (值,引用,指针)
#include iostream
#include string
using namespace std;class MyException
{
public:MyException() { cout MyException(): this endl; }MyException(const MyException) { cout MyException(const MyException): this endl; }~MyException() { cout ~MyException(): this endl; }void what() { cout MyException: this this endl; }
};class MyChildExcept : public MyException
{
public:MyChildExcept() { cout MyChildExcept(): this endl; }MyChildExcept(const MyChildExcept) { cout MyChildExcept(const MyChildExcept): this endl; }~MyChildExcept() { cout ~MyChildExcept(): this endl; }void what() { cout MyChildExcept: this this endl; }
};void func_local()
{// throw 局部对象MyException localEx;throw localEx; //尽管localEx是个局部对象但这里会将其复制构造出一个异常对象并存储在TIB中。而不是真正的将局部对象抛出去
}void func_temp()
{//throw 临时对象MyException(); //临时对象1throw MyException(); //编译器会将这个临时对象直接存储在线程TIB中成为异常对象注意与临时对象1存储位置一般相距较远
}void func_ptr()
{//throw 指针throw new MyException(); //注意异常对象是复制的堆对象而来的指针存在内存泄漏风险
}void func_again()
{MyChildExcept child;MyException re child; //注意抛出的是re的静态类型的异常对象即MyException而不是MyChildExcept;throw re;
}int main()
{cout ----------------------------------catch by value------------------------------ endl;//按值捕获try {func_local(); //throw MyExecption()}catch (MyException e) { //复制异常对象须额外进行一次拷贝cout catch (MyException e) endl;e.what();}cout --------------------------------catch by reference---------------------------- endl;//按引用捕获try {func_temp();}catch (MyException e) { //直接引用异常对象无须拷贝cout catch (MyException e) endl;e.what();}cout ---------------------------------catch by pointer----------------------------- endl;//按指针捕获try {func_ptr();}catch (MyException* e) { //按指针捕获可能造成内存泄漏cout catch (MyException* e) endl;e-what();delete e; //释放堆对象防止内存泄漏}cout ------------------------------------throw again------------------------------- endl;//二次抛异常try {try {func_again();}catch (MyException e) {e.what();//注意以下两种方式不同//1. 在throw后指定异常对象为e//throw e; //e会继续复制一份并抛出复制的异常对象而e本身会被释放//2.throw后不指定任何对象只要是在catch中捕获的一律抛出去。throw; //此时e本身再被抛出去。不会另外构造异常对象。}}catch (MyException e) {e.what();}return 0;
} 参考博客https://www.cnblogs.com/5iedu/p/11270922.html 异常的抛出和捕获 异常的抛出和匹配原则 异常是通过抛出对象而引发的该对象的类型决定了应该激活哪个catch的处理代码。被选中的处理代码是调用链中与该对象类型匹配且离抛出异常位置最近的那一个。抛出异常对象后会生成一个异常对象的拷贝因为抛出的异常对象可能是一个临时对象所以会生成一个拷贝对象这个拷贝的临时对象会在被catch以后销毁。这里的处理类似于函数的传值返回catch(...)可以捕获任意类型的异常问题是不知道异常错误是什么。实际中抛出和捕获的匹配原则有个例外并不都是类型完全匹配抛出的派生类对象可以使用基类捕获这个在实际中非常实用。 在函数调用链中异常栈展开匹配原则 首先检查throw本身是否在try块内部如果是再查找匹配的catch语句。如果有匹配的则调到catch的地方进行处理。没有匹配的catch则退出当前函数栈继续在调用函数的栈中进行查找匹配的catch。如果到达main函数的栈依旧没有匹配的则终止程序。上述这个沿着调用链查找匹配的catch子句的过程称为栈展开。所以实际中我们最后都要加一个catch(...)捕获任意类型的异常否则当有异常没捕获程序就会直接终止。4. 找到匹配的catch子句并处理以后会继续沿着catch子句后面继续执行。 //栈展开的测试#include iostream// 自定义异常类继承自std::exception类
class MyException : public std::exception {
public:// 重写what()方法以返回异常信息字符串const char* what() const noexcept override {return MyException: Something went wrong!;}
};// 函数func3抛出一个MyException异常
void func3() {std::cout func3: throwing MyException std::endl;throw MyException(); // 抛出一个MyException异常std::cout func3: return std::endl; //如果抛出异常,这里就不会执行}// 函数func2调用函数func3
void func2() {std::cout func2: calling func3 std::endl;func3(); // 调用函数func3std::cout func2: return std::endl;
}// 函数func1调用函数func2
void func1() {std::cout func1: calling func2 std::endl;func2(); // 调用函数func2std::cout func1: return std::endl;
}int main() {try {std::cout main: calling func1 std::endl;func1(); // 调用函数func1}catch (const std::exception e) { // 捕获一个std::exception类型的异常对象将异常对象赋值给变量estd::cerr Exception caught: e.what() std::endl; // 输出错误消息内容为捕获的异常信息}return 0; // 程序结束
} 异常的重新抛出 有可能单个的catch不能完全处理一个异常在进行一些校正处理以后希望再交给更外层的调用链函数来处理catch则可以通过重新抛出将异常传递给更上层的函数进行处理。 namespace skate{// 服务器开发中通常使用的异常继承体系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 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;};void SeedMsg(const string str){if (rand() (RAND_MAX /4)*3){throw HttpServerException(SeedMsg::网络错误, 2, put);}else if (rand() RAND_MAX /3){throw HttpServerException(SeedMsg::你已经不是对方好友, 1, post);}else{cout 消息发送成功- str endl;}}
}int main()
{srand(time(0));while (1){::Sleep(1000);try{//skate::HttpServer();// 发送出现网络错误要求重试3次// 权限错误就直接报错 for (size_t i 0; i 3; i){try{skate::SeedMsg(你好啊今晚一起看电影怎么样);break;}catch (const skate::Exception e){if (e.getid() 2) // 异常编码的价值针对某个错误进行特殊处理{cout 网络错误重试发消息第 i1次 endl; //特殊处理if (2 i) cout 网络错误发送失败 endl; //异常直接被捕获 不重新抛出 而是尝试重试continue; //网络错误,尝试重新发送 /} else // 其他错误{//break;//发送失败,直接重新抛出throw e; // 异常重新抛出 }}}}catch (const skate::Exception e) // 这里捕获父类对象就可以{// 多态cout e.what() endl; //此处已经捕获不到 网络错误,因为网络错误没有重新抛出,已经被特殊处理了}catch (const std::exception e) // 这里捕获父类对象就可以{// 多态cout e.what() endl;}catch (...){cout Unkown Exception endl;}}return 0;
} 《CPrimer》关于重新抛出 关于异常安全 构造函数完成对象的构造和初始化最好不要在构造函数中抛出异常否则可能导致对象不 完整或没有完全初始化析构函数主要完成资源的清理最好不要在析构函数内抛出异常否则可能导致资源泄漏(内存泄漏、句柄未关闭等)C中异常经常会导致资源泄漏的问题比如在new和delete中抛出了异常导致内存泄 漏在lock和unlock之间抛出了异常导致死锁C经常使用RAII来解决以上问题。 补充关于构造函数与try语句块 关于异常规范C11 noexcept 声明 C0x与C11异常规格声明方式的不同 void func() throw() { ... } // throw()声明该函数不会产生异常(C0x) void func() throw(int, double) { ... } //可能产生int或double类型异常(C0x) void func() noexcept { ... } // noexcept声明该函数不会产生异常(C11) void func() noexcept(常量表达式) { ... } //由表达式决定是否产生异常(C11) ps 这里学习noexcept关键字的关键主要为了看得懂官方文档的声明具体细节就做过多介绍了 -end