异常---异常安全问题--内存泄漏动图演示

异常是一种处理错误的方式,当一个函数发现自己无法处理的错误时就可以抛出异常,让函数的 直接或间接的调用者处理这个错误。

throw: 当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的。

catch: 在您想要处理问题的地方,通过异常处理程序捕获异常.catch 关键字用于捕获异 常,可以有多个catch进行捕获。

try: try 块中的代码标识将被激活的特定异常,它后面通常跟着一个或多个 catch 块。

如果有一个块抛出一个异常,捕获异常的方法会使用 try 和 catch 关键字。

 

void func1()
{
    int x, y;
    cin >> x >> y;
    if (y == 0)
    {
        throw "abc";
    }
    cout << x / y;
}


int main()
{
        try
        {
            func1();
        }
        catch (const char* c)
        {
            cout << c<<endl;
        }
}

这是一个普通的捕获异常的代码

一般发生除0错误时,程序会直接报错,终止程序

而为了不让程序直接终止,我们用异常捕获

 

throw:抛异常,可以抛任意类型的异常,抛出去时,会优先抛给进的函数,知道能捕获到它的异常为止,若都没有,则报错。

void func1()
{
    int x, y;
    cin >> x >> y;
    if (y == 0)
    {
        throw "abc";
    }
    cout << x / y;
}

void func2()
{
    try
    {
        func1();
    }
    catch (const char* c)
    {
        cout << c;
    }
}

int main()
{
        try
        {
            func1();
        }
        catch (const char* c)
        {
            cout << c<<endl;
        }
catch(...)
{
cout<<"异常错误";
}
return 0; }

这段代码 func2也try保护了func1  所以func1的异常,会优先被func2捕获。而不是被main捕获

catch(...)//这段代码是为了捕获没定义捕获的代码  也是能捕获任意类型的代码。但无法知道捕获过来的到底是哪里的异常。

 

  • 1.在抛出异常对象后,会生成异常对象的拷贝,因为抛出的异常对象,可能是临时对象,所以会生成一个拷贝对象,这个拷贝临时对象在被catch(捕获)后会销毁。
  • 2.可以抛出派生类对象,使用基类捕获,这个在实际非常实用

 

在函数调用链中异常栈展开匹配原则

  • 1.检查throw本身是否在try内,如果是,再召匹配的catch,若没有,则调用到catch的地方处理
  • 2.没有匹配的catch则退出当前函数栈,继续在调用函数栈中召匹配的catch
  • 3.如果到main函数找,依旧没有匹配,则终止程序,所以都需要再最后加上 catch(...) 捕获未知的异常
  • 4.找到匹配catch句子后,会直接catch内的所有句子。

 

异常编码的价值,针对某种错误进行特殊处理。

class A 

 {

 public:

    A(){}

};

    

void foo()

 { throw new A; }

这条代码 正常捕获 为:catch (A * x)

因为  异常是按照类型来捕获的,throw后抛出的是A*类型的异常,因此要按照指针方式进行捕获

 

异常安全问题

int func3(int x, int y)
{
    if (y == 0)
        throw "abc";

    return x / y;
}

int main()
{
    srand(time(0));
    //func2();
    while (1)
    {
        try
        {
            //func1();
            int* p = new int[1024*10];
            p[0] = rand();
            p[1] = rand() % 5;
            cout << func3(p[0], p[1])<<endl;
            delete p;
        }
        catch (const char* c)
        {
            cout << c<<endl;
        }
    }

    return 0;
}

这串代码的大致思路为,创建一个空间 然后在0和1放入随机值。放入func3函数内 如果y为0 会发生除0错误 然后捕获异常

那么原本的思路是 没发生异常  创建->计算->释放   发生异常时 创建->计算->发生异常捕获->释放

但是我们来看下它在任务管理器下发生的变化。

 

 这里我们发现,内存是不断增长的。 

这里可能有人会说,你开辟了空间肯定会涨啊,而且还是死循环。

记住,我确实是开辟了空间,但每次循环里我开辟好空间,计算后,都立刻释放空间。所以正常情况下,我们的内存是增长,然后释放,不断循环。是不会发生一直增长,而不释放的问题的。这就是内存泄漏问题。也就是本次的异常安全问题。

那么接下里。我来演示,正常情况,没有内存泄漏的问题。

 

 可以看到,此次是正常情况下,内存是一直保持在9.7MB 

那为啥会发生内存泄漏问题呢?

原因是  当发生了异常捕获时,那么我们的代码程序是会被throw直接抛出的,然后被catch捕获。那么本该执行delete代码,直接被跳过了,导致内存没有释放。再加上不断的循环,导致越来越多的内存没有释放,最终导致了内存泄漏问题。

也就是说 创建内存后  中间发生了异常,被抛出,程序直接走到了捕获异常那,而不是按照正常的代码逻辑走。 导致了最后没走到delete这段代码,没有成功释放。

 

C++没有垃圾回收记住,资源自己管理,有了异常非常容易导致内存泄漏、死锁等异常安全问题。需要用RAII来处理资源管理问题。

posted @ 2022-09-28 20:51  lemon-Breeze  阅读(48)  评论(0编辑  收藏  举报