如何在构造期间抛出bad_alloc时实现正确的堆栈展开和raii

时间:2017-05-19 07:39:05

标签: c++ memory-management raii bad-alloc stack-unwinding

所以我设计了一个会处理大量内存的类,我想确保它在内存分配过程中出现问题时能正确展开。

以下是我所拥有的:

class foo{
public:
    foo(): var1(nullptr), var2(nullptr){
        try{
        var1 = new int(1);
        var2 = new int(2);
        }
        catch(std::exception &e){
            delete var1;
            delete var2;
            return;
        }
    //Some other things involving these variables
}

如果我想在构造函数中的某些后续代码因bad_alloc异常导致错误之前想要停止构造此对象,我是否正确地在catch块中返回此内容?或者catch块在终止时是否取消构造函数?我是不是太过于复杂了?

2 个答案:

答案 0 :(得分:0)

  

我想确保它在构造函数的内存分配期间出现问题时能正确展开。

然后你必须使你的代码符合“一个类控制一个资源或做一个工作”的想法

#include <memory>

// foo has one job - to manage fooness. 
// It no longer manages memory resources

class foo
{
public:
    foo()
    : var1(new int(1))
    , var2(new int(2))
    {
    }

    std::unique_ptr<int> var1;   // unique_ptr only manages a resource
    std::unique_ptr<int> var2;   // unique_ptr only manages a resource
};

其他正确的初始化形式:

#include <memory>

// foo has one job - to manage fooness. 
// It no longer manages memory resources

class foo
{
public:
    foo()
    : var1(std::make_unique<int>(1))
    , var2(std::make_unique<int>(2))
    {
    }

    std::unique_ptr<int> var1;   // unique_ptr only manages a resource
    std::unique_ptr<int> var2;   // unique_ptr only manages a resource
};

默认值:

#include <memory>

// foo has one job - to manage fooness. 
// It no longer manages memory resources

class foo
{
public:
    // default constructor now implicit

    std::unique_ptr<int> var1 = std::make_unique<int>(1);   // unique_ptr only manages a resource
    std::unique_ptr<int> var2 = std::make_unique<int>(2);   // unique_ptr only manages a resource
};

正确,但习惯上不愉快 - 冗余初始化:

#include <memory>

// foo has one job - to manage fooness. 
// It no longer manages memory resources

class foo
{
public:
    foo()
    : var1(nullptr)
    , var2(nullptr)
    {
        var1 = std::make_unique<int>(1);
        var2 = std::make_unique<int>(2);
    }

    std::unique_ptr<int> var1;   // unique_ptr only manages a resource
    std::unique_ptr<int> var2;   // unique_ptr only manages a resource
};

这是没有构图的一种方法。注意没有收益的所有额外复杂性:

#include <memory>

// foo now has two jobs - to manage fooness, and to manage resources. 
// This adds un-necessary complication, bugs and maintenance overhead

class foo
{
public:
    foo()
    : var1(nullptr)
    , var2(nullptr)
    {
        var1 = new int(1);
        var2 = new int(2);
    }

    foo(const foo& other)
    : var1(nullptr)
    , var2(nullptr)
    {
        if (other.var1) {
            var1 = new int(*(other.var1));
        }

        if (other.var2) {
            var2 = new int(*(other.var2));
        }
    }

    foo(foo&& other) noexcept
    : var1(other.var1)
    , var2(other.var2)
    {
        other.var1 = nullptr;
        other.var2 = nullptr;
    }

    foo& operator=(const foo& other)
    {
        foo tmp(other);
        std::swap(var1, tmp.var1);
        std::swap(var2, tmp.var2);
        return *this;
    }

    foo& operator=(foo&& other) noexcept
    {
        foo tmp(std::move(other));
        std::swap(var1, tmp.var1);
        std::swap(var2, tmp.var2);
        return *this;
    }

    ~foo() noexcept
    {
        delete var1;
        delete var2;
    }

    int* var1;   // doesn't manage anything
    int* var2;   // doesn't manage anything
};

答案 1 :(得分:0)

你应该查看智能指针:std::unique_ptr(在这种情况下有用的那个)和std::shared_ptr / std::weak_ptr(不是你的例子的正确类型,但很好知道)。

只要您没有重新实现某种智能指针,使用它们而不是动态分配,它将为您节省一些麻烦(即使您的示例是正确的并且不会泄漏,只要析构函数是正确的)

不应该返回,而是应该抛出异常:应该通知调用者构造失败(如果分配失败意味着构造函数失败)。 RAII意味着对象永远不应处于无效状态。