1

因此,我正在设计一个将处理大量内存的类,并且我想确保它在其构造函数中的内存分配过程中出现问题时正确展开。

这是我所拥有的:

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 块在它终止时只是取消构造函数?我是不是把整个事情复杂化了?

4

2 回答 2

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
};
于 2017-05-19T07:49:06.303 回答
0

您应该检查智能指针:(std::unique_ptr在这种情况下会很有用)和std::shared_ptr/ std::weak_ptr(不是您的示例的正确类型,但很高兴知道)。

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

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

于 2017-05-19T07:51:29.637 回答