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;
};
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_ptr
s 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With