c++(6) 异常
内容目录:
1.异常概念
2.异常处理
3.异常接口声明
4.栈解旋
5.异常的生命周期
6.异常的多态
内容
1.异常概念
运行时错误是指程序在运行期间发生的错误,例如除数为 0、内存分配失败、数组越界、文件不存在等。C++ 异常(Exception)机制就是为解决运行时错误而引入的。
运行时错误如果放任不管,系统就会执行默认的操作,终止程序运行,也就是我们常说的程序崩溃(Crash)。C++ 提供了异常(Exception)机制,让我们能够捕获运行时错误,给程序一次“起死回生”的机会,或者至少告诉用户发生了什么再终止程序。
2.异常处理
1.异常模版
try{ // 可能抛出异常的语句 }catch(exceptionType variable){ // 处理异常的语句 }

#include <iostream> #include <string> #include <exception> using namespace std; int main(){ string str = "http://c.biancheng.net"; try{ char ch1 = str[100];//不抛出异常 cout<<ch1<<endl; }catch(exception e){ cout<<"[1]out of bound!"<<endl; } try{ char ch2 = str.at(100);(由string 抛出异常) cout<<ch2<<endl; }catch(exception &e){ //exception类位于<exception>头文件中 cout<<"[2]out of bound!"<<endl; } return 0; }
2.异常抛出
throw
关键字用来抛出一个异常,这个异常会被 try 检测到,进而被 catch 捕获。
3.异常类
catch(exceptionType variable)
exceptionType 异常类型
variable保持的异常信息
try抛出异常 由于catch接收 (这里可以多个catch)
所以异常的抛出需要由对应的异常来接收

try{ //可能抛出异常的语句 }catch (exception_type_1 e){ //处理异常的语句 }catch (exception_type_2 e){ //处理异常的语句 } //其他的catch catch (exception_type_n e){ //处理异常的语句 }
4.异常接口声明
在函数前 void test() throw(){函数体}
1.函数体throw -1 不能抛出任何异常
2.抛出个别异常 int ,float char
3. 抛出任何异常
5.栈解旋
异常被抛出后,从进入try块起,到异常被抛掷前,这期间在栈上构造的所有对象,都会被自动析构。析构的顺序与构造的顺序相反,这一过程称为栈的解旋(unwinding).

void ad() { mu<int> zq(1,2); throw 1; printf("测试"); } try { ad(); } catch (const std::exception&) { } catch (int ) { printf("收到了"); }
我的理解是throw抛出后 提升的堆栈被压回,数据自然消失

mu<int> zq(1,2); 00041D17 push 2 00041D19 push 1 00041D1B lea ecx,[zq] 00041D1E call std::endl<char,std::char_traits<char> > (04161Dh) 00041D23 mov dword ptr [ebp-4],0 52: throw 1; 00041D2A mov dword ptr [ebp-0E4h],1 00041D34 push offset __TI1H (04C45Ch) 00041D39 lea eax,[ebp-0E4h] 00041D3F push eax 00041D40 call __CxxThrowException@8 (0414F1h) //这里直接出call了 53: printf("测试"); 00041D45 push offset string "fa" (04ABD0h) 00041D4A call _printf (0415A5h) 00041D4F add esp,4 54: }
6.异常的生命周期
1.以【值方式】抛出和捕获匿名异常对象:调用拷贝构造函数创建匿名对象的拷贝
try { throw mu<int>(1,2); } catch (mu<int>z) { printf("收到了"); }
收到了析构了析构了函数结束 会调用拷贝构造 占内存 小问题 --
2.以【地址值方式】抛出和捕获异常对象:对象抛出立即被释放,对象指针指向被释放的内存
try { mu<int> zu(1, 2); throw &zu; } catch (mu<int>*z) { printf("收到了"); }
走完try语句块被析构 这个时候 mu<int>*z 指针指向的内存区域就是野指针
3.以【引用方式】抛出和捕获匿名异常对象:匿名对象的生命周期延续至左值(引用)
try { throw mu<int>(1, 2); } catch (mu<int>&z) { printf("收到了"); }
走完try catch语句块结束
4.以【地址值方式】(指针类型)抛出及捕获堆区匿名异常对象:堆区匿名对象需手动释放
try { throw new mu<int>(1, 2); } catch (mu<int>*z) { printf("收到了"); delete (z); }
需手动释放
7.异常的多态
异常的多态使用:catch语句中,使用基类的引用类型捕获子类异常对象。

class ttt { public: virtual void pr() = 0; }; class kzz :public ttt { public: virtual void pr() { printf("空指针"); } }; class szcw :public ttt { public: virtual void pr() { printf("数值错误"); } }; int main() { try { throw szcw(); throw kzz();//不会执行 } catch (ttt &mu) { mu.pr(); } }
8.编写自己的异常类
c++标准异常库

#include <iostream> using namespace std; #include <stdexcept> class MyOutOfRange : public exception { public: string errInfo; //记录错误提示信息 //带参构造函数 MyOutOfRange(const char* err) { //const char*可隐式转换为string this->errInfo = err; } //带参构造函数-重载 MyOutOfRange(const string& err) { this->errInfo = err; } //重写父类虚成员函数 virtual const char* what() const { return this->errInfo.c_str(); } }; class Person { public: int age; Person(int age) { //成员属性的有效性校验 if (age < 0 || age > 120) { string errStr = "string型参数:年龄值无效(0~120)"; throw MyOutOfRange(errStr); } else { this->age = age; } } }; void main() { try { Person p(150); } catch (exception& e) { //多态:使用父类引用接收子类异常对象 //const char* exception::what() 获取字符串标识异常 cout << e.what() << endl; //年龄值无效(0~120) } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步