I was reading this answer and the author refers to boost best practices which says:
Avoid using unnamed shared_ptr temporaries to save typing; to see why this is dangerous, consider this example:
void f(shared_ptr<int>, int);
int g();
void ok() {
shared_ptr<int> p(new int(2));
f(p, g());
}
void bad() {
f(shared_ptr<int>(new int(2)), g());
}
The function ok follows the guideline to the letter, whereas bad constructs the temporary shared_ptr in place, admitting the possibility of a memory leak. Since function arguments are evaluated in unspecified order, it is possible for new int(2) to be evaluated first, g() second, and we may never get to the shared_ptr constructor if g throws an exception. <...>
The exception safety problem described above may also be eliminated by using the make_shared or allocate_shared factory functions defined in boost/make_shared.hpp. These factory functions also provide an efficiency benefit by consolidating allocations.
I guess I will start using make_shared
, but I was wondering if this bit of advice still applies to C++11 shared_ptr
. I ask because I don't really fully understand why it is that a throwing g()
would prevent the ctor from getting called.
I don't think anything has changed with respect to the order of evaluation in C++11. That is, using std::make_shared<T>(...)
is the better option with respect to safety in addition to requiring only one memory allocation rather than two.
With respect to what the problem: the following evaluation of f()
argument is perfectly valid:
$tmp = new int(1)
(using $tmp
to indicate a compiler provided, temporary variable)g()
std::shared_ptr<int>($tmp)
Now, if g()
throws, the evaluation of the other parts is never executed, i.e., the std::shared_ptr<int>
is never constructed and $tmp
is leaked.
Yes, C++11's shared_ptr
works the same way.
I ask because I don't really fully understand why it is that a throwing g() would prevent the ctor from getting called.
What aren't you understanding? It's an order of operations problem, and the standard doesn't require a specific order. Let's unpack the statement into a sequence of expressions:
auto __temp = new int(2);
auto &&__temp2 = g();
auto __temp3 = shared_ptr<int>(__temp);
Do you see the problem now? If g
throws, then __temp3
will never be initialized. Therefore, __temp
will be leaked.
The C++ standard does not require that the statement be unpacked in this way. But it does not forbid it either. The compiler is allowed the freedom to order these independent expressions however it sees fit.
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