Smart Pointers
#include <memory> class MyResource; // specific typedef for smart pointer typedef std::unique_ptr<MyResource> MyResourceUp; ... // client code using resource through unique_ptr { // Aquire resource (e.g. create object) and store within unique_ptr MyResourceUp upMyResource (new MyResource ("some resource")); // Use resource through unique_ptr upMyResource->DoSomething(); } // upMyResource leaves scope -> MyResource is deleted // this works even in case of exceptions!
// assume myVector contains auto_ptr instances holding some resources // some std algorithms may contain code of the following type: std::auto_ptrtemp = myVector[i]; // unwanted transfer of ownership // Danger: this instruction moves the ownership from the vector element to // the temporary variable, when the temp variable gets out of scope the // corresponding resource is deleted.
unique_ptr does no longer allow this kind of error:
std::unique_ptrtemp = myVector[i]; // compile error // explicit usage of move semantics is needed: std::unique_ptr temp = std::move(myVector[i]);
Now you can safely use unique_ptr within containers and call any algorthms. If the container or algorithm uses (unwanted) copy semantics you will get a compile time error. If the container and algorithm uses move semantics all will work as desired.
class MyResource; typedef std::unique_ptr<MyResource> MyResourceUp; MyResourceUp CreateResource () { MyResourceUp up (new MyResource("Created Resource")); return up; } { MyResourceUp upMyResource; // simple assigning of new values, // implicitly using move semantics with transfer of ownership: // function return value is a rvalue upMyResource = CreateResource(); // temporary object is a rvalue upMyResource = MyResourceUp(new MyResource ("Resource B")); // explicit transfer of ownership needed MyResourceUp upOther; upOther = upMyResource; // will not compile, // upMyResource is an lvalue! // explicitly confirm move semantics upOther = std::move(upMyResource); // OK }
std::unique_ptr<int[]> up(new int[10]);With this definition the correct deleter "delete[]" will be called. Forgetting "[]" within definition of unique_ptr will compile without errors, but then destruction will no longer work correctly!
#include <memory> // class representing the resource class MyResource { public: void DoSomething(); }; // specific typedef for smart pointer typedef std::shared_ptr<MyResource> MyResourceSp; ... // client code using resource through shared_ptr { // Aquire resource (e.g. create object) and store within shared_ptr MyResourceSp spMyResource (new MyResource ("some resource")); // Use resource through shared_ptr spMyResource->DoSomething(); // Pass pointer to resource to some other component someOtherComponent.Transfer (spMyResource); // Remark: the other component may or may not store the // pointer for later use! } // spMyResource leaves scope -> reference count is decremented // only when count reaches 0 resources are freed/deleted
// custom deleter writing info to std::out before deleting shared_ptr<MyResource> spMyResource (new MyResource, [](MyResource* pMyRespource) { std::cout << "deleting " << pMyRespource->GetSomeInfo() std::endl; delete pMyResource });
// instead of MyResourceSp spMyResource (new MyResource ("some resource")); // write MyResourceSp spMyResource = std::make_shared<MyResource>("some resource");the arguments passed to make_shared are identical to the arguments you have defined as constructor arguments for your resource
// bad idea: MyResourceSp spMyResource(new MyResource); MyResource* pMyResource = spMyResource.get(); MyResourceSp spMyResource2(pMyresource); // spMyResource and spMyResurce2 don't know from each other and // both will try to delete/release the resource
A weak_ptr can be used to solve the problem of cyclic references:
Classes A and B use and own each other through sharewd_ptr instances. Without specific programing techniques
(e.g. shutdown protocol which leads to a release of all smart pointers) they will never be deleted.
// class representing the resource class MyResource { public: void DoSomething(); }; // specific typedef for smart pointers typedef std::shared_ptr<MyResource> MyResourceSp; typedef std::weak_ptr<MyResource> MyResourceWp; ... // Aquire resource (e.g. create object) and store within shared_ptr MyResourceSp spMyResource (new MyResource ("some resource")); // Store shared pointer to resource within weak pointer // (real code may pass the pointer to some other component which wants to use // the resource but does not want to keep the resource alive) MyResourceWp wpMyResource(spMyResource); ... // elsewhere in code // use the resource through weak_ptr { // create temporary shared_ptr and get ownership of resource // but be prepared: the resource may have been deleted! MyResourceSp spTmp = wpMyResource.lock(); if (spTmp) // Check if resource still exists { spTmp->DoSomething(); } } // relase temporary ownership by deletion of temp shared_ptr
All instances of shared_ptr and weak_ptr which are connected to the same resource depend on the same "control block" instance. The control block (as part of the std lib code) is responsible for reference counting and resource deletion.
When all shared_ptr instances are deleted/reset the regular reference count within the control block is decremented to 0 and the resource is deleted. All existing weak_ptr instances are still connected to the control block (see picture).
The control block will be deleted when the weak reference count is decremented to 0. This will happen when the last weak_ptr instance is reset.
class MyResource; typedef std::shared_ptr<MyResource> MyResourceSp; typedef std::weak_ptr<MyResource> MyResourceWp; { // Aquire resource (e.g. create object) and store within shared_ptr MyResourceSp spMyResource (new MyResource ("some resource")); // Use resource through shared_ptr spMyResource->DoSomething(); // Store pointer to resource within weak pointer // (real code may pass the pointer to some other component which wants to use // the resource but does not want to keep the resource alive) MyResourceWp wpMyResource(spMyResource); // Using resource through weak_ptr is only possible after converting to shared_ptr { Check("After initialization", spMyResource, wpMyResource); MyResourceSp spTmp = wpMyResource.lock(); Check("Use count is incremented to 2 because a temporary shared_ptr " "holds a reference to resource", spMyResource, wpMyResource); if (spTmp) // Check if shared_ptr points to something { spTmp->DoSomething(); } } // temp shared_ptr goes out of scope, refcount is decremented Check("temp shared_ptr has gone out of scope", spMyResource, wpMyResource); spMyResource.reset(); Check("shared_ptr has been reset", spMyResource, wpMyResource); // store new resource within spMyResource# // the original resource is released automatically spMyResource.reset(new MyResource("another resource")); // the weak pointer originally connected with spMyRespource stays invalid // because the original resource has been deleted // (technically the weak pointer stays connected with the control block // for the already deleted resource) Check("shared_ptr now stores new resource", spMyResource, wpMyResource); } // "another resource" gets released when spMyResource gets out of scope
some resource: Constructor some resource: doing something Info: After initialization shared_ptr owns some resource useCount=1 weak_ptr points to some resource useCount=1 Info: Use count is incremented to 2 because a temporary shared_ptr holds a reference to resource shared_ptr owns some resource useCount=2 weak_ptr points to some resource useCount=2 some resource: doing something Info: temp shared_ptr has gone out of scope shared_ptr owns some resource useCount=1 weak_ptr points to some resource useCount=1 some resource: Destructor Info: shared_ptr has been reset shared_ptr does not own a resource weak_ptr points to null another resource: Constructor Info: shared_ptr now stores new resource shared_ptr owns another resource useCount=1 weak_ptr points to null another resource: Destructor
class MyResource { public: // Constructor MyResource (std::string const & in_name) : m_name (in_name) { std::cout << m_name.c_str() << ": Constructor" << std::endl; } // Destructor ~MyResource (void) { std::cout << m_name.c_str() << ": Destructor" << std::endl; } // Write name to stdout void DoSomething (void) { std::cout << m_name.c_str() << ": doing something" << std::endl; } std::string GetName (void) {return m_name;} private: std::string m_name; };
void CheckSharedPtr (MyResourceSp& in_spResource) { if (!in_spResource) { std::cout << "shared_ptr does not own a resource" << std::endl; } else { std::cout << "shared_ptr owns " << in_spResource->GetName().c_str() << " useCount=" << in_spResource.use_count() << std::endl; } } void CheckWeakPtr (MyResourceWp& in_wpResource) { long useCount = in_wpResource.use_count(); MyResourceSp spTmp = in_wpResource.lock(); if (!spTmp) { std::cout << "weak_ptr points to null" << std::endl; } else { std::cout << "weak_ptr points to " << spTmp->GetName().c_str() << " useCount=" << useCount << std::endl; } } void Check (std::string const & in_info, MyResourceSp& in_spResource, MyResourceWp& in_wpResource) { std::cout << std::endl << "Info: " << in_info.c_str() << std::endl; CheckSharedPtr (in_spResource); CheckWeakPtr (in_wpResource); }