Consider my code below. My understanding of unique pointers was that only one unique pointer can be used to reference one variable or object. In my code I have more than one uni
Just an addition to Christian Hackl's excellent answer:
std::unique_ptr
was introduced to ensure RAII for pointers; this means, in opposite to raw pointers you don't have to take care about destruction yourself anymore. The whole management of the raw pointer is done by the smart pointer. Leaks caused by a forgotten delete
can not happen anymore.
If a std::unique_ptr
would only allow to be created by std::make_unique
, it would be absolutely safe regarding allocation and deallocation, and of course that would be also detectable during compile time.
But that's not the case: std::unique_ptr
is also constructible with a raw pointer. The reason is, that being able to be constructed with a hard pointer makes a std::unique_ptr
much more useful. If this would not be possible, e.g. the pointer returned by Christian Hackl's function_with_runtime_input()
would not be possible to integrate into a modern RAII environment, you would have to take care of destruction yourself.
Of course the downside with this is that errors like yours can happen: To forget destruction is not possible with std::unique_ptr
, but erroneous multiple destructions are always possible (and impossible to track by the compiler, as C.H. already said), if you created it with a raw pointer constructor argument. Always be aware that std::unique_ptr
logically takes "ownership" of the raw pointer - what means, that no one else may delete the pointer except the one std::unique_ptr
itself.
As rules of thumb it can be said:
std::unique_ptr
with std::make_unique
if possible.std::unique_ptr
with it.std::unique_ptr
takes ownership of the supplied raw pointerval
in your example).std::unique_ptr
only with raw pointers, which were created by new
, if possible.std::unique_ptr
needs to be constructed with a raw pointer, which was created by something else than new
, add a custom deleter to the std::unique_ptr
, which matches the hard pointer creator. An example are image pointers in the (C based) FreeImage library, which always have to be destroyed by FreeImage_Unload()
Some examples to these rules:
// Safe
std::unique_ptr p = std::make_unique();
// Safe, but not advisable. No accessible raw pointer exists, but should use make_unique.
std::unique_ptr p(new int());
// Handle with care. No accessible raw pointer exists, but it has to be sure
// that function_with_runtime_input() allocates the raw pointer with 'new'
std::unique_ptr p( function_with_runtime_input() );
// Safe. No accessible raw pointer exists,
// the raw pointer is created by a library, and has a custom
// deleter to match the library's requirements
struct FreeImageDeleter {
void operator() (FIBITMAP* _moribund) { FreeImage_Unload(_moribund); }
};
std::unique_ptr p( FreeImage_Load(...) );
// Dangerous. Your class method gets a raw pointer
// as a parameter. It can not control what happens
// with this raw pointer after the call to MyClass::setMySomething()
// - if the caller deletes it, your'e lost.
void MyClass::setMySomething( MySomething* something ) {
// m_mySomethingP is a member std::unique_ptr
m_mySomethingP = std::move( std::unique_ptr( something ));
}
// Dangerous. A raw pointer variable exists, which might be erroneously
// deleted multiple times or assigned to a std::unique_ptr multiple times.
// Don't touch iPtr after these lines!
int* iPtr = new int();
std::unique_ptr p(iPtr);
// Wrong (Undefined behaviour) and a direct consequence of the dangerous declaration above.
// A raw pointer is assigned to a std::unique_ptr twice, which means
// that it will be attempted to delete it twice.
// This couldn't have happened if iPtr wouldn't have existed in the first
// place, like shown in the 'safe' examples.
int* iPtr = new int();
std::unique_ptr p(iPtr);
std::unique_ptr p2(iPtr);
// Wrong. (Undefined behaviour)
// An unique pointer gets assigned a raw pointer to a stack variable.
// Erroneous double destruction is the consequence
int val;
int* valPtr = &val;
std::unique_ptr p(valPtr);