Talk:cpp/language/raii
[edit] Smart pointers are not a good representative of RAII.
Most uses of smart pointers (except for make_shared and make_unique), including the FILE* example on this site, require checking the smart pointer after construction. Bjarne Stroustrup said:
Consider an error detected in a constructor; how do you report the error? You throw an exception. That's the basis of RAII and that often requires the acquisition of resources. Imagine that we did not have exceptions, how would you deal with an error detected in a constructor? Remember that constructors are often invoked initialize/construct objects in variables:
The vector constructor could either set the variable into a "bad" state (as ifstream does by default) so that every subsequent operation fails. That's not ideal.
vector<double> v(100000); // needs to allocate memory
if (v.bad()) { /* handle error */ } // vector doesn't actually have a bad(); it relies on exceptions
That's an extra test per object (to write, to remember or forget). This gets really messy for classes composed of several objects, especially if those sub-objects depend on each other.
According to N3588, there is still a possibility of leakage when using unique_ptr:
Second, make_unique prevents the unspecified-evaluation-order leak triggered by expressions like foo(unique_ptr<X>(new X), unique_ptr<Y>(new Y)). (Following the advice "never say new" is simpler than "never say new, unless you immediately give it to a named unique_ptr".)
so std::make_unique was added to the standard. C++17 fixed this possibility, but this problem is unique to smart pointers.
--YexuanXiao (talk) 07:22, 23 January 2024 (PST)
- Checking pointers for null is annoying, but it doesn't violate RAII, the lifetime of the resource is correctly bound to an object that cleans up after itself. The lack of exception safety in the expression foo(unique_ptr<X>(new X), unique_ptr<Y>(new Y)) has nothing to do with unique_ptr, pick any RAII type you like and you can construct the same issue, e.g. foo(vector<T>{new arg0, new arg1}, vector<T>{new arg2}). The fact that not all expressions involving unique_ptr are safe doesn't make them poor representations of RAII. --Ybab321 (talk) 09:57, 23 January 2024 (PST)
- The difference is that the constructors of unique_ptr never fails, while vector's can. vectors can still throw an exception and leak memory if any explicit new in foo(vector<T>{new arg0, new arg1}, vector<T>{new arg2}) succeeds, so this comparison is unfair. It is safe to use a vector arbitrarily without any explicit resource allocation, while unique_ptr cannot. The improved way is to use make_unique: foo(make_unique<X>(), make_unique<Y>());, it never leaks and this pattern is also applicable in C++98.--YexuanXiao (talk) 10:49, 23 January 2024 (PST)
- regardless of our feelings about it, unique_ptr with non-default deleter to manage resources obtained from OpenSSL, sqlite, libxml, DPDK, ffmpeg, SDL, WinAPI, and other C API libraries that use pointer-typed handles is a widely used idiom that is called, colloquially, "RAII wrapper". It belongs to the page called "RAII". I agree from guidelines standpoint that a better RAII wrapper would be a named class perhaps with such unique_ptr as its member to cut down boilerplate - in that sense I'd perhaps call them "RAII wrapper building blocks", but it's not what everyone calls them. --Cubbi (talk) 11:31, 23 January 2024 (PST)