Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Two-phase construction takes the following shape:

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;
};

The point of which being that a naive constructor (that doesn't watch for allocation failures) will leak memory: a partially-constructed object's destructor is never called.

My question: is the following code safe, and by corrolary, do smart pointers obviate two-phase construction?

struct something {
    something ()
      : p1(new int(2))
      , p2(new int(5))
    {   }

    std::unique_ptr<int> p1;
    std::unique_ptr<int> p2;
};
like image 259
Andres Jaan Tack Avatar asked Nov 09 '11 12:11

Andres Jaan Tack


1 Answers

Yes, your new code is fine. Note however that there is a possible subtly in more complex cases:

#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;
}

would not be safe however since the order of evaluation of the arguments of the constructor of foo in the initializer list of bar is unspecified. A conforming compiler might choose to do a depth or breadth first order of evaluation (or anything else so long as they all got evaluated correctly in the end). This means that if the first new int succeeded, but the second one throws before the shared_ptr objects got constructed the first allocation to be performed could still leak.

If you find yourself wanting to do this there are two possible solutions, besides just falling back to two-phase construction: the first might be a refactor, the second would be to construct the shared_ptrs individually first as members of bar, before f. Which of those is most appropriate is a judgment call I think needs to be made on a case by case basis.

like image 67
Flexo Avatar answered Nov 14 '22 23:11

Flexo