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){
    //处理异常的语句
}
多层catch

 

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)
    }
}
自写异常

 

posted @ 2023-05-23 15:08  大橘|博客  阅读(24)  评论(1)    收藏  举报