类的构造方法中的异常会导致内存泄漏吗? [英] Does Exception in Constructor of class can lead to memory leak?

查看:74
本文介绍了类的构造方法中的异常会导致内存泄漏吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

存在内存泄漏,因为异常后未调用B构造函数.如果该构造函数分配了其他类的对象,并且某个时刻发生异常,则需要释放所有先前分配的对象,或者有一种自动管理该异常的方法,那么在该构造函数中处理异常的最佳方法是什么.

There is memory leak as B constructor not called after exception. What is best way to handle exception in constructor if that constructor allocates objects of other class and if at some point exception occurs it need to free all previously allocated objects or is there is way to manage this automatically.

#include <iostream>
#include<utility>
using namespace std;

class A
{
private:
    int *eleA;
public:
    A()
    {
        eleA = new int;
        //*eleA = 0;
        cout<<"Constructor A\n";
    }
    ~A()
    {
        cout<<"Destructor A \n";
        delete eleA;
        eleA = nullptr;
    }
};
class B
{
private:
    int *eleB;

public:
    B()
    {
        cout<<"Constructor B\n";
        eleB = new int;
        //*eleB = 0;
        throw -1; //Exception thrown
    }
    ~B()
    {
        cout<<"Destructor B \n";
        delete eleB;
        eleB = nullptr;
    }
};

class AB
{
private:
    A *a_ptr;
    B *b_ptr;
public:

    AB() : a_ptr(nullptr),b_ptr(nullptr)
    {
      try{
        cout<<"Constructor AB \n";
        a_ptr = new A;
        b_ptr = new B;
      }
      catch(int a)
      {
          cout<<"Exception catched\n"; 
      }
    }
    ~AB()
    {
        cout<<"Destructor AB \n";
        if (a_ptr)
        {
            delete a_ptr;
            a_ptr = nullptr;
        }
        if (b_ptr)
        {
            delete b_ptr;
            b_ptr = nullptr;
        }
    }
};
int main()
{
    AB *ab = new AB;

    delete ab;

    return 0;
}

输出:

构造函数AB

构造函数A

构造函数B

捕获到异常

Destructor AB

Destructor AB

析构函数A

推荐答案

显而易见的答案是使用智能指针".将 int * eleA; 替换为 std :: unique_ptr< int>eleA; ,将 eleA = new int; 替换为 eleA = std :: make_unique< int>(); ,删除 delete eleA; ,就可以完成操作(将其他指针重复同样的操作).这样的对象将被自动删除,因此您不必担心泄漏.

The obvious answer is "use smart pointers". Replace int *eleA; with std::unique_ptr<int> eleA;, replace eleA = new int; with eleA = std::make_unique<int>();, remove delete eleA;, and you're done (repeat the same thing for other pointers you have). Such objects will be deleted automatically, so you don't have to worry about leaks.

但是,知道一个可用于管理任意资源(不仅是指针)的通用解决方案仍然是一件好事,例如C API的各种句柄.该解决方案是 护目镜

But it's still good to know a generic solution that you can use to manage arbitrary resources (not only pointers), e.g. various handles from C APIs. That solution is scope guards!

它们不是标准的C ++,但是实现起来很容易,因此有无数的库提供它们.你可以自己写;示例实现位于答案的结尾.

They are not in standard C++, but implementing them is easy, so there are countless libraries that provide them. You can write them yourself; an example implementation is at the end of the answer.

这是您的使用方式:

int main()
{
    int *ptr = new int;
    FINALLY( delete ptr; )
    // Do something with `ptr` here.
}

FINALLY()是一个宏,用于创建范围保护.这个想法是 delete ptr; 不会立即执行,而是在控制离开当前作用域时执行,即到达结束括号} 或由于异常而执行.这意味着,无论任何例外情况,您都无需担心删除 ptr .

FINALLY() is a macro that creates a scope guard. The idea is that delete ptr; is not executed immediately, but rather is executed when control leaves the current scope, i.e. either reaches the closing brace }, or because of an exception. This means that you no longer need to worry about deleting ptr, regardless of any exceptions.

我们如何将其应用于您的构造函数?我们创建了一个不同的宏,该宏仅在由于异常而离开作用域时才运行代码:

How do we apply this to your constructor? We make a different macro, that only runs code if you leave the scope because of an exception:

B()
{
    cout<<"Constructor B\n";
    eleB = new int;
    FINALLY_ON_THROW( delete eleB; )
    *eleB = 0;
    throw -1; //Exception thrown
}

这可以概括为任意数量的资源.创建完每个示波器后,您只需放置一个这样的示波器后卫即可.

This generalizes to any number of resources. You just put such a scope guard after creating each one of them.

范围卫实现示例:

#include <exception>
#include <utility>

namespace Macro
{
    template <typename T> class ScopeGuard
    {
        T func;
      public:
        ScopeGuard(T &&func) : func(std::move(func)) {}
        ScopeGuard(const ScopeGuard &) = delete;
        ScopeGuard &operator=(const ScopeGuard &) = delete;
        ~ScopeGuard() {func();}
    };

    // Same as `ScopeGuard`, but can throw.
    template <typename T> class ScopeGuardExc
    {
        T func;
      public:
        ScopeGuardExc(T &&func) : func(std::move(func)) {}
        ScopeGuardExc(const ScopeGuardExc &) = delete;
        ScopeGuardExc &operator=(const ScopeGuardExc &) = delete;
        ~ScopeGuardExc() noexcept(false) {func();}
    };
}

#define FINALLY_impl_cat(a, b) FINALLY_impl_cat_(a, b)
#define FINALLY_impl_cat_(a, b) a##b

#define FINALLY(...) \
    ::Macro::ScopeGuard FINALLY_impl_cat(_finally_object_,__LINE__) \
    ([&]() -> void { __VA_ARGS__ });

#define FINALLY_ON_THROW(...) \
    ::Macro::ScopeGuard FINALLY_impl_cat(_finally_object_,__LINE__) \
    ([&, _finally_exc_depth_ = ::std::uncaught_exceptions()]() -> void { if (::std::uncaught_exceptions() > _finally_exc_depth_) {__VA_ARGS__} });

#define FINALLY_ON_SUCCESS(...) \
    ::Macro::ScopeGuardExc FINALLY_impl_cat(_finally_object_,__LINE__) \
    ([&, _finally_exc_depth_ = ::std::uncaught_exceptions()]() -> void { if (::std::uncaught_exceptions() <= _finally_exc_depth_) {__VA_ARGS__} });

这篇关于类的构造方法中的异常会导致内存泄漏吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆