智能指针是否排除了两阶段构造的需要?

Do smart pointers preclude the need for two-phase construction?

本文关键字:段构造 指针 是否 排除 智能      更新时间:2023-10-16

两阶段结构采用以下形式:

struct something {
    something ()
      : p1(NULL)
      , p2(NULL)
    {   }
    ~something () {
        if (p1) delete p1;
        if (p2) delete p2;
    }
    void initialize () {
        p1 = new int(2);
        p2 = new int(5);    // May throw if allocation fails!
    }
    int* p1;
    int* p2;
};

简单构造函数(不监视分配失败)会泄漏内存:部分构造对象的析构函数永远不会被调用。

我的问题是:下面的代码安全吗?根据推论,智能指针是否可以避免两阶段构造?

struct something {
    something ()
      : p1(new int(2))
      , p2(new int(5))
    {   }
    std::unique_ptr<int> p1;
    std::unique_ptr<int> p2;
};

是的,您的新代码很好。但是请注意,在更复杂的情况下可能会有一个微妙的

#include <memory>
struct foo {
  foo(std::shared_ptr<int> a, std::shared_ptr<int> b) { }
};
struct bar {
  foo f;
  bar() : f(std::shared_ptr<int>(new int), std::shared_ptr<int>(new int)) { }
};
int main() {
  bar b;
}
但是

不是安全的,因为bar的初始化列表中foo的构造函数的参数的求值顺序是未指定的。符合标准的编译器可能会选择进行深度或广度的第一阶求值(或者其他任何东西,只要它们最终都得到了正确的求值)。这意味着如果第一个new int成功,但是第二个在shared_ptr对象构造之前抛出,那么执行的第一个分配仍然可能泄漏。

如果您发现自己想要这样做,除了回到两阶段构造之外,还有两种可能的解决方案:第一种可能是重构,第二种可能是首先将shared_ptr单独构造为bar的成员,然后再构造f。哪一种最合适,我认为需要根据具体情况做出判断。

我的问题是:下面的代码安全吗,

可以,可以。

struct something {
    something ()
      : p(new int(5))
    {   }
    std::unique_ptr<int> p;
};

注意幼稚代码

struct something {
    something ()
      : p(new int(5))
    {   }
    int* p;
};

也是异常安全的,因为只有一个分配会失败。我想你说的是

struct something {
    something ()
      : p(new int(5)), q(new int)
    {   }
    int *p, *q;
};

不会。在这种情况下,智能指针也可以工作。

如果您只是处理异常,则根本不需要两阶段构造。这是RIAA的方式。

struct something {
    something ()
      : p1(NULL)
      , p2(NULL)
    {   
        p1 = new int(2);
        try {
            p2 = new int(5);    // May throw if allocation fails!
        } catch (std::bad_alloc&) {
            delete p1;  //cleanup 
            throw;  //rethrow
        }
    }
    ~something () {
        delete p1;
        delete p2;
    }
    int* p1;
    int* p2;
};