From c8a366399f69010141457f1443cbb2b967818aa9 Mon Sep 17 00:00:00 2001 From: Marcus Mohr <marcus.mohr@lmu.de> Date: Fri, 3 Feb 2023 15:05:30 +0100 Subject: [PATCH] Finish first complete draft of notebook #15 for class next Monday --- notebooks/15_SmartPointers.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebooks/15_SmartPointers.ipynb b/notebooks/15_SmartPointers.ipynb index 7fa84b2..6cb4618 100644 --- a/notebooks/15_SmartPointers.ipynb +++ b/notebooks/15_SmartPointers.ipynb @@ -1 +1 @@ -{"metadata":{"language_info":{"codemirror_mode":"text/x-c++src","file_extension":".cpp","mimetype":"text/x-c++src","name":"c++","version":"17"},"kernelspec":{"name":"xcpp17","display_name":"C++17","language":"C++17"}},"nbformat_minor":5,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Smart Pointers","metadata":{},"id":"cf0bbc25-707e-4d3a-88b2-5a2559000469"},{"cell_type":"markdown","source":"A very helpful addition to C++ was the introduction of so called **smart pointers** in C++11. These are the template classes:\n- `std::shared_ptr`\n- `std::unique_ptr`\n- `std::weak_ptr`\n---\nConceptually an object that dynamically allocates some kind of resource should also take care of deallocating that resource again. However, if the object needs to allow access to that address from the outside for some reason, it get's difficult. A common situation is that multiple objects, not necessarily of the same class, want to share a resource. In that situation an object must not deallocate it, if there are still others around that use it.\n\nAs a motivation we are going to try to implement a *wrapper class* for a pointer to some shared resource which handles the 'reference counting' and performs the deallocation. The resource\nwill be passed to the constructor from the outside. So the specification would be something like:\n- constructor accepts pointer to resource\n- objects should be copyable to allow sharing of resource\n- wrapper class keeps track on how many objects sharing the same resource (i.e. holding the same pointer) exist\n- when an object is destroyed it should deallocate the resource, if it is the last one holding it","metadata":{},"id":"68ec4466-5b37-4c08-b2a0-e2729b0cd0eb"},{"cell_type":"markdown","source":"**Brainstorming:**<br>\nHow can we implement this? What ingredients should we use?\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>","metadata":{},"id":"a04f1904-d672-41c6-9d55-5c3c66323947"},{"cell_type":"markdown","source":"Our wrapper class should allow to generate objects holding resources of different type. One way to accomplish this is to use a template class.","metadata":{},"id":"8806369d-2415-435d-9649-302478522064"},{"cell_type":"code","source":"template< typename T >\nclass ResourcePointer {\n \nprivate:\n T* rawPtr_ = nullptr;\n};","metadata":{"trusted":true},"execution_count":1,"outputs":[],"id":"e5fac784-75bb-4fa8-9561-0c57d24f6a31"},{"cell_type":"markdown","source":"- We want to allocate the resource outside of our wrapper and initialise it with its address.\n- Hence we should not allow the default c'tor\n- In order to avoid problems we will nullify the pointer variable we receive.","metadata":{},"id":"657d6dd0-e73c-4b93-a5d8-ad6daeec11fc"},{"cell_type":"code","source":"template< typename T >\nclass ResourcePointer {\n\npublic:\n ResourcePointer() = delete;\n ResourcePointer( T*& ptr ) : rawPtr_( ptr ) {\n // resource now belongs to us!\n ptr = nullptr;\n }\n \nprivate:\n T* rawPtr_ = nullptr;\n};","metadata":{"trusted":true},"execution_count":2,"outputs":[],"id":"c3d46bb1-c839-4655-9d65-31485c63969a"},{"cell_type":"markdown","source":"As the constructed object holds the ressource's address now, it should increase the reference count!\n\nCounting over multiple objects of the same class can be accomplished with a static member (class variable). However, one member will not be sufficient, since we need to distinguish groups of objects with different ressources.\n\nA map can help us here. Since a pointer stores a memory address, which is a form of integer, we can directly use that as a **key**. The associated **value** will be our reference count.","metadata":{},"id":"a937bd6e-1f2c-4ae2-b84b-5f233ccae8fc"},{"cell_type":"code","source":"template< typename T >\nclass ResourcePointer {\n\npublic:\n ResourcePointer() = delete;\n ResourcePointer( T*& ptr ) : rawPtr_( ptr ) {\n // resource now belongs to us!\n ptr = nullptr;\n }\n \nprivate:\n T* rawPtr_ = nullptr;\n static std::map< T*, int > ptrCounts_;\n};","metadata":{"trusted":true},"execution_count":3,"outputs":[],"id":"2b18526d-d8c9-4b3d-843f-3594a8976676"},{"cell_type":"markdown","source":"We need to initialise the static member outside of the class, due to its type.","metadata":{},"id":"18481050-4e02-4f20-928b-4aaf4992371f"},{"cell_type":"code","source":"template< typename T >\nstd::map< T*, int > ResourcePointer<T>::ptrCounts_ = std::map< T*, int >();","metadata":{"trusted":true},"execution_count":4,"outputs":[],"id":"fa335307-a14b-4550-8f6c-a2fd12ed9130"},{"cell_type":"markdown","source":"Now we can update our constructor:","metadata":{},"id":"cd36b937-050e-4ec8-b44c-e9beb5669fb0"},{"cell_type":"code","source":"template< typename T >\nclass ResourcePointer {\n\npublic:\n ResourcePointer() = delete;\n ResourcePointer( T*& ptr ) : rawPtr_( ptr ) {\n \n // while this should not happen, better check whether there\n // are already objects that hold this resource\n if( ptrCounts_.find( rawPtr_ ) != ptrCounts_.end() ) {\n // increment reference counter\n ptrCounts_[ rawPtr_ ]++;\n }\n else {\n // insert (key,value) pair into map\n ptrCounts_.insert( { rawPtr_, 1 } );\n }\n\n // resource now belongs to us!\n ptr = nullptr;\n }\n \nprivate:\n T* rawPtr_ = nullptr;\n static std::map< T*, int > ptrCounts_;\n};","metadata":{"trusted":true},"execution_count":5,"outputs":[],"id":"76634dc4-9760-48af-9b0c-df8e55f4e07b"},{"cell_type":"markdown","source":"Next step is to implement the destructor. It should deallocate the resource, if and only if, the object being destroyed is the last one owning it.","metadata":{},"id":"99d41dbc-0f9f-46e7-a212-0e7c6c6c5e15"},{"cell_type":"code","source":"template< typename T >\nclass ResourcePointer {\n\npublic:\n ResourcePointer() = delete;\n ResourcePointer( T*& ptr ) : rawPtr_( ptr ) {\n \n // while this should not happen, better check whether there\n // are already objects that hold this resource\n if( ptrCounts_.find( rawPtr_ ) != ptrCounts_.end() ) {\n // increment reference counter\n ptrCounts_[ rawPtr_ ]++;\n }\n else {\n // insert (key,value) pair into map\n ptrCounts_.insert( { rawPtr_, 1 } );\n }\n\n // resource now belongs to us!\n ptr = nullptr;\n }\n\n ~ResourcePointer() {\n\n // check whether we still own the ressource\n if( rawPtr_ != nullptr ) {\n\n // delete resource, if we are the last one\n int count = ptrCounts_.at( rawPtr_ ) - 1;\n if( count == 0 ) {\n\n // object pointer or array pointer?\n if( std::is_array_v< decltype( rawPtr_ ) > ) {\n delete[] rawPtr_;\n }\n else {\n delete rawPtr_;\n }\n }\n\n // update reference count\n ptrCounts_.at( rawPtr_ ) = count;\n }\n }\n\nprivate:\n T* rawPtr_ = nullptr;\n static std::map< T*, int > ptrCounts_;\n};","metadata":{"trusted":true},"execution_count":6,"outputs":[],"id":"6648676d-e964-414d-9aa6-58319f64abfc"},{"cell_type":"markdown","source":"Okay, the foundations are laid. However, to allow sharing we now implement a copy constructor and copy assignment operator","metadata":{},"id":"7404da4f-7e7b-4fc5-8ffc-66d612601c69"},{"cell_type":"code","source":"template< typename T >\nclass ResourcePointer {\n\npublic:\n ResourcePointer() = delete;\n ResourcePointer( T*& ptr ) : rawPtr_( ptr ) {\n \n // while this should not happen, better check whether there\n // are already objects that hold this resource\n if( ptrCounts_.find( rawPtr_ ) != ptrCounts_.end() ) {\n // increment reference counter\n ptrCounts_[ rawPtr_ ]++;\n }\n else {\n // insert (key,value) pair into map\n ptrCounts_.insert( { rawPtr_, 1 } );\n }\n\n // resource now belongs to us!\n ptr = nullptr;\n }\n\n // copy constructor\n ResourcePointer( const ResourcePointer< T >& other ) {\n rawPtr_ = other.rawPtr_;\n ptrCounts_.at( rawPtr_ )++;\n }\n\n // copy assignment\n ResourcePointer< T >& operator=( const ResourcePointer< T >& other ) {\n ResourcePointer< T > newPtr;\n newPtr.rawPtr_ = other.rawPtr_;\n ptrCounts_.at( rawPtr_ )++;\n return newPtr;\n }\n\n ~ResourcePointer() {\n\n // check whether we still own the ressource\n if( rawPtr_ != nullptr ) {\n\n // delete resource, if we are the last one\n int count = ptrCounts_.at( rawPtr_ ) - 1;\n if( count == 0 ) {\n\n // object pointer or array pointer?\n if( std::is_array_v< decltype( rawPtr_ ) > ) {\n delete[] rawPtr_;\n }\n else {\n delete rawPtr_;\n }\n }\n\n // update reference count\n ptrCounts_.at( rawPtr_ ) = count;\n }\n }\n\nprivate:\n T* rawPtr_ = nullptr;\n static std::map< T*, int > ptrCounts_;\n};","metadata":{"trusted":true},"execution_count":7,"outputs":[],"id":"4951abc1-158c-413f-9e14-e889df36571f"},{"cell_type":"markdown","source":"**What's missing?**<br><br>\nWell, we implemented\n- destructor\n- copy constructor\n- copy assignment operator\n\nThus, following the **rule-of-five/six**, we should also implement\n\n- move constructor\n- move assignment operator\n\nas we already deleted the default constructor.","metadata":{},"id":"d2abd86f-c6c2-416a-98f5-16e502360e4f"},{"cell_type":"code","source":"template< typename T >\nclass ResourcePointer {\n\npublic:\n ResourcePointer() = delete;\n ResourcePointer( T*& ptr ) : rawPtr_( ptr ) {\n \n // while this should not happen, better check whether there\n // are already objects that hold this resource\n if( ptrCounts_.find( rawPtr_ ) != ptrCounts_.end() ) {\n // increment reference counter\n ptrCounts_[ rawPtr_ ]++;\n }\n else {\n // insert (key,value) pair into map\n ptrCounts_.insert( { rawPtr_, 1 } );\n }\n\n // resource now belongs to us!\n ptr = nullptr;\n }\n\n // copy constructor\n ResourcePointer( const ResourcePointer< T >& other ) {\n rawPtr_ = other.rawPtr_;\n ptrCounts_.at( rawPtr_ )++;\n }\n\n // copy assignment\n ResourcePointer< T >& operator=( const ResourcePointer< T >& other ) {\n ResourcePointer< T > newPtr;\n newPtr.rawPtr_ = other.rawPtr_;\n ptrCounts_.at( rawPtr_ )++;\n return newPtr;\n }\n\n // move constructor\n ResourcePointer( ResourcePointer< T >&& other ) {\n rawPtr_ = other.rawPtr_;\n other.rawPtr_ = nullptr;\n };\n\n // move assignment\n ResourcePointer< T >& operator=( ResourcePointer< T >&& other ) {\n ResourcePointer< T > newPtr;\n newPtr.rawPtr_ = other.rawPtr_;\n other.rawPtr_ = nullptr;\n return newPtr;\n };\n \n // destructor\n ~ResourcePointer() {\n\n // check whether we still own the ressource\n if( rawPtr_ != nullptr ) {\n\n // delete resource, if we are the last one\n int count = ptrCounts_.at( rawPtr_ ) - 1;\n if( count == 0 ) {\n\n // object pointer or array pointer?\n if( std::is_array_v< decltype( rawPtr_ ) > ) {\n delete[] rawPtr_;\n }\n else {\n delete rawPtr_;\n }\n }\n\n // update reference count\n ptrCounts_.at( rawPtr_ ) = count;\n }\n }\n\nprivate:\n T* rawPtr_ = nullptr;\n static std::map< T*, int > ptrCounts_;\n};","metadata":{"trusted":true},"execution_count":17,"outputs":[{"name":"stdout","text":"Writing ResourcePointer.hpp\n","output_type":"stream"}],"id":"c18a33a8-0444-4fb6-86e3-f8db5703e3c6"},{"cell_type":"markdown","source":"Finally we add a getter method `getCount()` to showcase the class' functionality","metadata":{},"id":"d395e36d-9235-467f-9fa5-72a72ab3a7ee"},{"cell_type":"code","source":"%%file ResourcePointer.hpp\n\ntemplate< typename T >\nclass ResourcePointer {\n\npublic:\n ResourcePointer() = delete;\n ResourcePointer( T*& ptr ) : rawPtr_( ptr ) {\n \n // while this should not happen, better check whether there\n // are already objects that hold this resource\n if( ptrCounts_.find( rawPtr_ ) != ptrCounts_.end() ) {\n // increment reference counter\n ptrCounts_[ rawPtr_ ]++;\n }\n else {\n // insert (key,value) pair into map\n ptrCounts_.insert( { rawPtr_, 1 } );\n }\n\n // resource now belongs to us!\n ptr = nullptr;\n }\n\n // copy constructor\n ResourcePointer( const ResourcePointer< T >& other ) {\n rawPtr_ = other.rawPtr_;\n ptrCounts_.at( rawPtr_ )++;\n }\n\n // copy assignment\n ResourcePointer< T >& operator=( const ResourcePointer< T >& other ) {\n ResourcePointer< T > newPtr;\n newPtr.rawPtr_ = other.rawPtr_;\n ptrCounts_.at( rawPtr_ )++;\n return newPtr;\n }\n\n ~ResourcePointer() {\n\n // check whether we still own the ressource\n if( rawPtr_ != nullptr ) {\n\n // delete resource, if we are the last one\n int count = ptrCounts_.at( rawPtr_ ) - 1;\n if( count == 0 ) {\n\n // object pointer or array pointer?\n if( std::is_array_v< decltype( rawPtr_ ) > ) {\n delete[] rawPtr_;\n }\n else {\n delete rawPtr_;\n }\n }\n\n // update reference count\n ptrCounts_.at( rawPtr_ ) = count;\n }\n }\n\n int getCount() const {\n return ptrCounts_.at( rawPtr_ );\n }\n\nprivate:\n T* rawPtr_ = nullptr;\n static std::map< T*, int > ptrCounts_;\n};\n\ntemplate< typename T >\nstd::map< T*, int > ResourcePointer<T>::ptrCounts_ = std::map< T*, int >();","metadata":{"trusted":true},"execution_count":27,"outputs":[{"name":"stdout","text":"Overwriting ResourcePointer.hpp\n","output_type":"stream"}],"id":"4ccd404f-bb31-44ec-b8a5-91361bc4abf6"},{"cell_type":"markdown","source":"Now let us implement a driver to test our class","metadata":{},"id":"60213dfc-fd96-4a54-acf1-f550c3bdfa0c"},{"cell_type":"code","source":"%%file driver.cpp\n\n#include <type_traits>\n#include <iostream>\n#include <map>\n\n#include \"ResourcePointer.hpp\"\n\nint main() {\n\n double* array = new double[100];\n std::cout << \" array = \" << array << std::endl;\n\n ResourcePointer< double > ptr1( array );\n std::cout << \" array = \" << array << std::endl;\n\n ResourcePointer ptr2{ ptr1 };\n std::cout << \"Resource used by \" << ptr1.getCount() << \" objects\"\n << std::endl;\n\n for( int k = 0; k < 5; ++k ) {\n ResourcePointer localPtr = ptr1;\n std::cout << \"k = \" << k << \": Resource used by \" << localPtr.getCount()\n << \" objects\" << std::endl;\n }\n\n std::cout << \"Resource used by \" << ptr1.getCount() << \" objects\"\n << std::endl;\n\n ResourcePointer ptr3{ std::move( ptr2 ) };\n std::cout << \"Resource used by \" << ptr1.getCount() << \" objects\"\n << std::endl;\n \n std::string* strPtr = new std::string;\n *strPtr = \"Shared String\";\n ResourcePointer stringRes{ strPtr };\n std::cout << \"String Resource used by \" << stringRes.getCount() << \" objects\"\n << std::endl;\n}\n","metadata":{"trusted":true},"execution_count":36,"outputs":[{"name":"stdout","text":"Overwriting driver.cpp\n","output_type":"stream"}],"id":"1b5bd85b-372a-4e04-bbfe-4c75b4920d28"},{"cell_type":"code","source":"!g++ -std=c++17 driver.cpp","metadata":{"trusted":true},"execution_count":37,"outputs":[],"id":"05933e8c-c764-4cf1-a2e3-02a541f3da95"},{"cell_type":"code","source":"!./a.out","metadata":{"trusted":true},"execution_count":38,"outputs":[{"name":"stdout","text":" array = 0x557ed1e53e70\n array = 0\nResource used by 2 objects\nk = 0: Resource used by 3 objects\nk = 1: Resource used by 3 objects\nk = 2: Resource used by 3 objects\nk = 3: Resource used by 3 objects\nk = 4: Resource used by 3 objects\nResource used by 2 objects\nResource used by 3 objects\nString Resource used by 1 objects\n","output_type":"stream"}],"id":"ac21d9d3-4169-4e8e-a998-c307f2c52790"},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[],"id":"c3c74078-5fb3-46df-9fb2-6a2fe2f12d2c"}]} \ No newline at end of file +{"metadata":{"kernelspec":{"display_name":"C++17","language":"C++17","name":"xcpp17"},"language_info":{"codemirror_mode":"text/x-c++src","file_extension":".cpp","mimetype":"text/x-c++src","name":"c++","version":"17"}},"nbformat_minor":5,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Smart Pointers","metadata":{},"id":"cf0bbc25-707e-4d3a-88b2-5a2559000469"},{"cell_type":"markdown","source":"A very helpful addition to C++ was the introduction of so called **smart pointers** in C++11. These are the template classes:\n- `std::shared_ptr`\n- `std::unique_ptr`\n- `std::weak_ptr`\n---\nConceptually an object that dynamically allocates some kind of resource should also take care of deallocating that resource again. However, if the object needs to allow access to that address from the outside for some reason, it get's difficult. A common situation is that multiple objects, not necessarily of the same class, want to share a resource. In that situation an object must not deallocate it, if there are still others around that use it.\n","metadata":{},"id":"68ec4466-5b37-4c08-b2a0-e2729b0cd0eb"},{"cell_type":"markdown","source":"## Example: Homebrew Wrapper Class\nAs a motivation we are going to try to implement a *wrapper class* for a pointer to some shared resource which handles the 'reference counting' and performs the deallocation. The resource\nwill be passed to the constructor from the outside. So the specification would be something like:\n- constructor accepts pointer to resource\n- objects should be copyable to allow sharing of resource\n- wrapper class keeps track on how many objects sharing the same resource (i.e. holding the same pointer) exist\n- when an object is destroyed it should deallocate the resource, if it is the last one holding it","metadata":{},"id":"bb3560a7-c700-4f03-84da-0deb89fef0b9"},{"cell_type":"markdown","source":"**Brainstorming:**<br>\nHow can we implement this? What ingredients should we use?\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>","metadata":{},"id":"a04f1904-d672-41c6-9d55-5c3c66323947"},{"cell_type":"markdown","source":"Our wrapper class should allow to generate objects holding resources of different type. One way to accomplish this is to use a template class.","metadata":{},"id":"8806369d-2415-435d-9649-302478522064"},{"cell_type":"code","source":"template< typename T >\nclass ResourcePointer {\n \nprivate:\n T* rawPtr_ = nullptr;\n};","metadata":{"trusted":true},"execution_count":1,"outputs":[],"id":"e5fac784-75bb-4fa8-9561-0c57d24f6a31"},{"cell_type":"markdown","source":"- We want to allocate the resource outside of our wrapper and initialise it with its address.\n- Hence we should not allow the default c'tor\n- In order to avoid problems we will nullify the pointer variable we receive.","metadata":{},"id":"657d6dd0-e73c-4b93-a5d8-ad6daeec11fc"},{"cell_type":"code","source":"template< typename T >\nclass ResourcePointer {\n\npublic:\n ResourcePointer() = delete;\n ResourcePointer( T*& ptr ) : rawPtr_( ptr ) {\n // resource now belongs to us!\n ptr = nullptr;\n }\n \nprivate:\n T* rawPtr_ = nullptr;\n};","metadata":{"trusted":true},"execution_count":2,"outputs":[],"id":"c3d46bb1-c839-4655-9d65-31485c63969a"},{"cell_type":"markdown","source":"As the constructed object holds the ressource's address now, it should increase the reference count!\n\nCounting over multiple objects of the same class can be accomplished with a static member (class variable). However, one member will not be sufficient, since we need to distinguish groups of objects with different ressources.\n\nA map can help us here. Since a pointer stores a memory address, which is a form of integer, we can directly use that as a **key**. The associated **value** will be our reference count.","metadata":{},"id":"a937bd6e-1f2c-4ae2-b84b-5f233ccae8fc"},{"cell_type":"code","source":"template< typename T >\nclass ResourcePointer {\n\npublic:\n ResourcePointer() = delete;\n ResourcePointer( T*& ptr ) : rawPtr_( ptr ) {\n // resource now belongs to us!\n ptr = nullptr;\n }\n \nprivate:\n T* rawPtr_ = nullptr;\n static std::map< T*, int > ptrCounts_;\n};","metadata":{"trusted":true},"execution_count":3,"outputs":[],"id":"2b18526d-d8c9-4b3d-843f-3594a8976676"},{"cell_type":"markdown","source":"We need to initialise the static member outside of the class, due to its type.","metadata":{},"id":"18481050-4e02-4f20-928b-4aaf4992371f"},{"cell_type":"code","source":"template< typename T >\nstd::map< T*, int > ResourcePointer<T>::ptrCounts_ = std::map< T*, int >();","metadata":{"trusted":true},"execution_count":4,"outputs":[],"id":"fa335307-a14b-4550-8f6c-a2fd12ed9130"},{"cell_type":"markdown","source":"Now we can update our constructor:","metadata":{},"id":"cd36b937-050e-4ec8-b44c-e9beb5669fb0"},{"cell_type":"code","source":"template< typename T >\nclass ResourcePointer {\n\npublic:\n ResourcePointer() = delete;\n ResourcePointer( T*& ptr ) : rawPtr_( ptr ) {\n \n // while this should not happen, better check whether there\n // are already objects that hold this resource\n if( ptrCounts_.find( rawPtr_ ) != ptrCounts_.end() ) {\n // increment reference counter\n ptrCounts_[ rawPtr_ ]++;\n }\n else {\n // insert (key,value) pair into map\n ptrCounts_.insert( { rawPtr_, 1 } );\n }\n\n // resource now belongs to us!\n ptr = nullptr;\n }\n \nprivate:\n T* rawPtr_ = nullptr;\n static std::map< T*, int > ptrCounts_;\n};","metadata":{"trusted":true},"execution_count":5,"outputs":[],"id":"76634dc4-9760-48af-9b0c-df8e55f4e07b"},{"cell_type":"markdown","source":"Next step is to implement the destructor. It should deallocate the resource, if and only if, the object being destroyed is the last one owning it.","metadata":{},"id":"99d41dbc-0f9f-46e7-a212-0e7c6c6c5e15"},{"cell_type":"code","source":"template< typename T >\nclass ResourcePointer {\n\npublic:\n ResourcePointer() = delete;\n ResourcePointer( T*& ptr ) : rawPtr_( ptr ) {\n \n // while this should not happen, better check whether there\n // are already objects that hold this resource\n if( ptrCounts_.find( rawPtr_ ) != ptrCounts_.end() ) {\n // increment reference counter\n ptrCounts_[ rawPtr_ ]++;\n }\n else {\n // insert (key,value) pair into map\n ptrCounts_.insert( { rawPtr_, 1 } );\n }\n\n // resource now belongs to us!\n ptr = nullptr;\n }\n\n ~ResourcePointer() {\n\n // check whether we still own the ressource\n if( rawPtr_ != nullptr ) {\n\n // delete resource, if we are the last one\n int count = ptrCounts_.at( rawPtr_ ) - 1;\n if( count == 0 ) {\n\n // object pointer or array pointer?\n if( std::is_array_v< decltype( rawPtr_ ) > ) {\n delete[] rawPtr_;\n }\n else {\n delete rawPtr_;\n }\n }\n\n // update reference count\n ptrCounts_.at( rawPtr_ ) = count;\n }\n }\n\nprivate:\n T* rawPtr_ = nullptr;\n static std::map< T*, int > ptrCounts_;\n};","metadata":{"trusted":true},"execution_count":6,"outputs":[],"id":"6648676d-e964-414d-9aa6-58319f64abfc"},{"cell_type":"markdown","source":"Okay, the foundations are laid. However, to allow sharing we now implement a copy constructor and copy assignment operator","metadata":{},"id":"7404da4f-7e7b-4fc5-8ffc-66d612601c69"},{"cell_type":"code","source":"template< typename T >\nclass ResourcePointer {\n\npublic:\n ResourcePointer() = delete;\n ResourcePointer( T*& ptr ) : rawPtr_( ptr ) {\n \n // while this should not happen, better check whether there\n // are already objects that hold this resource\n if( ptrCounts_.find( rawPtr_ ) != ptrCounts_.end() ) {\n // increment reference counter\n ptrCounts_[ rawPtr_ ]++;\n }\n else {\n // insert (key,value) pair into map\n ptrCounts_.insert( { rawPtr_, 1 } );\n }\n\n // resource now belongs to us!\n ptr = nullptr;\n }\n\n // copy constructor\n ResourcePointer( const ResourcePointer< T >& other ) {\n rawPtr_ = other.rawPtr_;\n ptrCounts_.at( rawPtr_ )++;\n }\n\n // copy assignment\n ResourcePointer< T >& operator=( const ResourcePointer< T >& other ) {\n ResourcePointer< T > newPtr;\n newPtr.rawPtr_ = other.rawPtr_;\n ptrCounts_.at( rawPtr_ )++;\n return newPtr;\n }\n\n ~ResourcePointer() {\n\n // check whether we still own the ressource\n if( rawPtr_ != nullptr ) {\n\n // delete resource, if we are the last one\n int count = ptrCounts_.at( rawPtr_ ) - 1;\n if( count == 0 ) {\n\n // object pointer or array pointer?\n if( std::is_array_v< decltype( rawPtr_ ) > ) {\n delete[] rawPtr_;\n }\n else {\n delete rawPtr_;\n }\n }\n\n // update reference count\n ptrCounts_.at( rawPtr_ ) = count;\n }\n }\n\nprivate:\n T* rawPtr_ = nullptr;\n static std::map< T*, int > ptrCounts_;\n};","metadata":{"trusted":true},"execution_count":7,"outputs":[],"id":"4951abc1-158c-413f-9e14-e889df36571f"},{"cell_type":"markdown","source":"**What's missing?**<br><br>\nWell, we implemented\n- destructor\n- copy constructor\n- copy assignment operator\n\nThus, following the **rule-of-five/six**, we should also implement\n\n- move constructor\n- move assignment operator\n\nas we already deleted the default constructor.","metadata":{},"id":"d2abd86f-c6c2-416a-98f5-16e502360e4f"},{"cell_type":"code","source":"template< typename T >\nclass ResourcePointer {\n\npublic:\n ResourcePointer() = delete;\n ResourcePointer( T*& ptr ) : rawPtr_( ptr ) {\n \n // while this should not happen, better check whether there\n // are already objects that hold this resource\n if( ptrCounts_.find( rawPtr_ ) != ptrCounts_.end() ) {\n // increment reference counter\n ptrCounts_[ rawPtr_ ]++;\n }\n else {\n // insert (key,value) pair into map\n ptrCounts_.insert( { rawPtr_, 1 } );\n }\n\n // resource now belongs to us!\n ptr = nullptr;\n }\n\n // copy constructor\n ResourcePointer( const ResourcePointer< T >& other ) {\n rawPtr_ = other.rawPtr_;\n ptrCounts_.at( rawPtr_ )++;\n }\n\n // copy assignment\n ResourcePointer< T >& operator=( const ResourcePointer< T >& other ) {\n ResourcePointer< T > newPtr;\n newPtr.rawPtr_ = other.rawPtr_;\n ptrCounts_.at( rawPtr_ )++;\n return newPtr;\n }\n\n // move constructor\n ResourcePointer( ResourcePointer< T >&& other ) {\n rawPtr_ = other.rawPtr_;\n other.rawPtr_ = nullptr;\n };\n\n // move assignment\n ResourcePointer< T >& operator=( ResourcePointer< T >&& other ) {\n ResourcePointer< T > newPtr;\n newPtr.rawPtr_ = other.rawPtr_;\n other.rawPtr_ = nullptr;\n return newPtr;\n };\n \n // destructor\n ~ResourcePointer() {\n\n // check whether we still own the ressource\n if( rawPtr_ != nullptr ) {\n\n // delete resource, if we are the last one\n int count = ptrCounts_.at( rawPtr_ ) - 1;\n if( count == 0 ) {\n\n // object pointer or array pointer?\n if( std::is_array_v< decltype( rawPtr_ ) > ) {\n delete[] rawPtr_;\n }\n else {\n delete rawPtr_;\n }\n }\n\n // update reference count\n ptrCounts_.at( rawPtr_ ) = count;\n }\n }\n\nprivate:\n T* rawPtr_ = nullptr;\n static std::map< T*, int > ptrCounts_;\n};","metadata":{"trusted":true},"execution_count":8,"outputs":[],"id":"c18a33a8-0444-4fb6-86e3-f8db5703e3c6"},{"cell_type":"markdown","source":"Finally we add a getter method `getCount()` to showcase the class' functionality","metadata":{},"id":"d395e36d-9235-467f-9fa5-72a72ab3a7ee"},{"cell_type":"code","source":"%%file ResourcePointer.hpp\n\ntemplate< typename T >\nclass ResourcePointer {\n\npublic:\n ResourcePointer() = delete;\n ResourcePointer( T*& ptr ) : rawPtr_( ptr ) {\n \n // while this should not happen, better check whether there\n // are already objects that hold this resource\n if( ptrCounts_.find( rawPtr_ ) != ptrCounts_.end() ) {\n // increment reference counter\n ptrCounts_[ rawPtr_ ]++;\n }\n else {\n // insert (key,value) pair into map\n ptrCounts_.insert( { rawPtr_, 1 } );\n }\n\n // resource now belongs to us!\n ptr = nullptr;\n }\n\n // copy constructor\n ResourcePointer( const ResourcePointer< T >& other ) {\n rawPtr_ = other.rawPtr_;\n ptrCounts_.at( rawPtr_ )++;\n }\n\n // copy assignment\n ResourcePointer< T >& operator=( const ResourcePointer< T >& other ) {\n ResourcePointer< T > newPtr;\n newPtr.rawPtr_ = other.rawPtr_;\n ptrCounts_.at( rawPtr_ )++;\n return newPtr;\n }\n\n // move constructor\n ResourcePointer( ResourcePointer< T >&& other ) {\n rawPtr_ = other.rawPtr_;\n other.rawPtr_ = nullptr;\n };\n\n // move assignment\n ResourcePointer< T >& operator=( ResourcePointer< T >&& other ) {\n ResourcePointer< T > newPtr;\n newPtr.rawPtr_ = other.rawPtr_;\n other.rawPtr_ = nullptr;\n return newPtr;\n };\n \n ~ResourcePointer() {\n\n // check whether we still own the ressource\n if( rawPtr_ != nullptr ) {\n\n // delete resource, if we are the last one\n int count = ptrCounts_.at( rawPtr_ ) - 1;\n if( count == 0 ) {\n\n // object pointer or array pointer?\n if( std::is_array_v< decltype( rawPtr_ ) > ) {\n delete[] rawPtr_;\n }\n else {\n delete rawPtr_;\n }\n }\n\n // update reference count\n ptrCounts_.at( rawPtr_ ) = count;\n }\n }\n\n int getCount() const {\n return ptrCounts_.at( rawPtr_ );\n }\n\nprivate:\n T* rawPtr_ = nullptr;\n static std::map< T*, int > ptrCounts_;\n};\n\ntemplate< typename T >\nstd::map< T*, int > ResourcePointer<T>::ptrCounts_ = std::map< T*, int >();","metadata":{"trusted":true},"execution_count":9,"outputs":[{"name":"stdout","output_type":"stream","text":"Overwriting ResourcePointer.hpp\n"}],"id":"4ccd404f-bb31-44ec-b8a5-91361bc4abf6"},{"cell_type":"markdown","source":"Now let us implement a driver to test our class","metadata":{},"id":"60213dfc-fd96-4a54-acf1-f550c3bdfa0c"},{"cell_type":"code","source":"%%file driver.cpp\n\n#include <type_traits>\n#include <iostream>\n#include <map>\n\n#include \"ResourcePointer.hpp\"\n\nint main() {\n\n double* array = new double[100];\n std::cout << \" array = \" << array << std::endl;\n\n ResourcePointer< double > ptr1( array );\n std::cout << \" array = \" << array << std::endl;\n\n ResourcePointer ptr2{ ptr1 };\n std::cout << \"Resource used by \" << ptr1.getCount() << \" objects\"\n << std::endl;\n\n for( int k = 0; k < 5; ++k ) {\n ResourcePointer localPtr = ptr1;\n std::cout << \"k = \" << k << \": Resource used by \" << localPtr.getCount()\n << \" objects\" << std::endl;\n }\n\n std::cout << \"Resource used by \" << ptr1.getCount() << \" objects\"\n << std::endl;\n\n ResourcePointer ptr3{ std::move( ptr2 ) };\n std::cout << \"Resource used by \" << ptr1.getCount() << \" objects\"\n << std::endl;\n \n std::string* strPtr = new std::string;\n *strPtr = \"Shared String\";\n ResourcePointer stringRes{ strPtr };\n std::cout << \"String Resource used by \" << stringRes.getCount() << \" objects\"\n << std::endl;\n}\n","metadata":{"trusted":true},"execution_count":10,"outputs":[{"name":"stdout","output_type":"stream","text":"Overwriting driver.cpp\n"}],"id":"1b5bd85b-372a-4e04-bbfe-4c75b4920d28"},{"cell_type":"code","source":"!g++ -std=c++17 driver.cpp","metadata":{"trusted":true},"execution_count":11,"outputs":[],"id":"05933e8c-c764-4cf1-a2e3-02a541f3da95"},{"cell_type":"code","source":"!./a.out","metadata":{"trusted":true},"execution_count":12,"outputs":[{"name":"stdout","output_type":"stream","text":" array = 0x556502c5fe70\n array = 0\nResource used by 2 objects\nk = 0: Resource used by 3 objects\nk = 1: Resource used by 3 objects\nk = 2: Resource used by 3 objects\nk = 3: Resource used by 3 objects\nk = 4: Resource used by 3 objects\nResource used by 2 objects\nResource used by 2 objects\nString Resource used by 1 objects\n"}],"id":"ac21d9d3-4169-4e8e-a998-c307f2c52790"},{"cell_type":"markdown","source":"How about directly creating the resource as part of the constructor call?<br>","metadata":{},"id":"f174b5d3-47a1-43fd-9d42-11206add558d"},{"cell_type":"code","source":"%%file driver.cpp\n\n#include <type_traits>\n#include <iostream>\n#include <map>\n\n#include \"ResourcePointer.hpp\"\n\nint main() {\n ResourcePointer stringRes{ new std::string };\n}","metadata":{"trusted":true},"execution_count":13,"outputs":[{"name":"stdout","output_type":"stream","text":"Overwriting driver.cpp\n"}],"id":"c3c74078-5fb3-46df-9fb2-6a2fe2f12d2c"},{"cell_type":"code","source":"!g++ -std=c++17 driver.cpp","metadata":{"trusted":true},"execution_count":14,"outputs":[{"name":"stdout","output_type":"stream","text":"driver.cpp: In function ‘int main()’:\ndriver.cpp:9:46: error: class template argument deduction failed:\n ResourcePointer stringRes{ new std::string };\n ^\ndriver.cpp:9:46: error: no matching function for call to ‘ResourcePointer(std::__cxx11::string*)’\nIn file included from driver.cpp:6:0:\nResourcePointer.hpp:39:5: note: candidate: template<class T> ResourcePointer(ResourcePointer<T>&&)-> ResourcePointer<T>\n ResourcePointer( ResourcePointer< T >&& other ) {\n ^~~~~~~~~~~~~~~\nResourcePointer.hpp:39:5: note: template argument deduction/substitution failed:\ndriver.cpp:9:46: note: mismatched types ‘ResourcePointer<T>’ and ‘std::__cxx11::string* {aka std::__cxx11::basic_string<char>*}’\n ResourcePointer stringRes{ new std::string };\n ^\nIn file included from driver.cpp:6:0:\nResourcePointer.hpp:25:5: note: candidate: template<class T> ResourcePointer(const ResourcePointer<T>&)-> ResourcePointer<T>\n ResourcePointer( const ResourcePointer< T >& other ) {\n ^~~~~~~~~~~~~~~\nResourcePointer.hpp:25:5: note: template argument deduction/substitution failed:\ndriver.cpp:9:46: note: mismatched types ‘const ResourcePointer<T>’ and ‘std::__cxx11::string* {aka std::__cxx11::basic_string<char>*}’\n ResourcePointer stringRes{ new std::string };\n ^\nIn file included from driver.cpp:6:0:\nResourcePointer.hpp:7:5: note: candidate: ResourcePointer(T*&)-> ResourcePointer<T> [with T = std::__cxx11::basic_string<char>] <near match>\n ResourcePointer( T*& ptr ) : rawPtr_( ptr ) {\n ^~~~~~~~~~~~~~~\nResourcePointer.hpp:7:5: note: conversion of argument 1 would be ill-formed:\ndriver.cpp:9:30: error: cannot bind non-const lvalue reference of type ‘std::__cxx11::basic_string<char>*&’ to an rvalue of type ‘std::__cxx11::string* {aka std::__cxx11::basic_string<char>*}’\n ResourcePointer stringRes{ new std::string };\n ^~~~~~~~~~~~~~~\nIn file included from driver.cpp:6:0:\nResourcePointer.hpp:6:5: note: candidate: template<class T> ResourcePointer()-> ResourcePointer<T>\n ResourcePointer() = delete;\n ^~~~~~~~~~~~~~~\nResourcePointer.hpp:6:5: note: template argument deduction/substitution failed:\ndriver.cpp:9:46: note: candidate expects 0 arguments, 1 provided\n ResourcePointer stringRes{ new std::string };\n ^\nIn file included from driver.cpp:6:0:\nResourcePointer.hpp:3:7: note: candidate: template<class T> ResourcePointer(ResourcePointer<T>)-> ResourcePointer<T>\n class ResourcePointer {\n ^~~~~~~~~~~~~~~\nResourcePointer.hpp:3:7: note: template argument deduction/substitution failed:\ndriver.cpp:9:46: note: mismatched types ‘ResourcePointer<T>’ and ‘std::__cxx11::string* {aka std::__cxx11::basic_string<char>*}’\n ResourcePointer stringRes{ new std::string };\n ^\n"}],"id":"79288989-7e03-4676-b80e-3947d498d2a6"},{"cell_type":"markdown","source":"---\nOkay, we need to help the compiler here:","metadata":{},"id":"e576f5ba-3ecc-4aff-aa80-a522f3a9142a"},{"cell_type":"code","source":"%%file driver.cpp\n\n#include <type_traits>\n#include <iostream>\n#include <map>\n\n#include \"ResourcePointer.hpp\"\n\nint main() {\n ResourcePointer< std::string > stringRes1{ new std::string };\n // ResourcePointer< std::string > stringRes2 = new std::string;\n}","metadata":{"trusted":true},"execution_count":16,"outputs":[{"name":"stdout","output_type":"stream","text":"Overwriting driver.cpp\n"}],"id":"fac2d20e-c9a4-4bf4-a018-578e58b2e279"},{"cell_type":"code","source":"!g++ -std=c++17 driver.cpp","metadata":{"trusted":true},"execution_count":17,"outputs":[{"name":"stdout","output_type":"stream","text":"driver.cpp: In function ‘int main()’:\ndriver.cpp:9:46: error: cannot bind non-const lvalue reference of type ‘std::__cxx11::basic_string<char>*&’ to an rvalue of type ‘std::__cxx11::string* {aka std::__cxx11::basic_string<char>*}’\n ResourcePointer< std::string > stringRes1{ new std::string };\n ^~~~~~~~~~~~~~~\nIn file included from driver.cpp:6:0:\nResourcePointer.hpp:7:5: note: initializing argument 1 of ‘ResourcePointer<T>::ResourcePointer(T*&) [with T = std::__cxx11::basic_string<char>]’\n ResourcePointer( T*& ptr ) : rawPtr_( ptr ) {\n ^~~~~~~~~~~~~~~\n"}],"id":"1a547da1-2167-40fc-9037-07d6e3c6fb8e"},{"cell_type":"markdown","source":"This is more critical. The object we create in the constructor call is a temporary one, so an **r-value**. However, there is no constructor that accepts an r-value of type `T*`. Hence, we need to implement one:","metadata":{},"id":"96e6b67b-ec0d-4b2b-8347-6c3d29522dbc"},{"cell_type":"code","source":"%%file ResourcePointer.hpp\n\ntemplate< typename T >\nclass ResourcePointer {\n\npublic:\n ResourcePointer() = delete;\n ResourcePointer( T*& ptr ) : rawPtr_( ptr ) {\n \n // while this should not happen, better check whether there\n // are already objects that hold this resource\n if( ptrCounts_.find( rawPtr_ ) != ptrCounts_.end() ) {\n // increment reference counter\n ptrCounts_[ rawPtr_ ]++;\n }\n else {\n // insert (key,value) pair into map\n ptrCounts_.insert( { rawPtr_, 1 } );\n }\n\n // resource now belongs to us!\n ptr = nullptr;\n }\n\n ResourcePointer( T*&& ptr ) : rawPtr_( ptr ) {\n\n std::cout << \"r-value c'tor called!\" << std::endl;\n \n // while this should not happen, better check whether there\n // are already objects that hold this resource\n if( ptrCounts_.find( rawPtr_ ) != ptrCounts_.end() ) {\n // increment reference counter\n ptrCounts_[ rawPtr_ ]++;\n }\n else {\n // insert (key,value) pair into map\n ptrCounts_.insert( { rawPtr_, 1 } );\n }\n }\n\n // copy constructor\n ResourcePointer( const ResourcePointer< T >& other ) {\n rawPtr_ = other.rawPtr_;\n ptrCounts_.at( rawPtr_ )++;\n }\n\n // copy assignment\n ResourcePointer< T >& operator=( const ResourcePointer< T >& other ) {\n ResourcePointer< T > newPtr;\n newPtr.rawPtr_ = other.rawPtr_;\n ptrCounts_.at( rawPtr_ )++;\n return newPtr;\n }\n\n // move constructor\n ResourcePointer( ResourcePointer< T >&& other ) {\n rawPtr_ = other.rawPtr_;\n other.rawPtr_ = nullptr;\n };\n\n // move assignment\n ResourcePointer< T >& operator=( ResourcePointer< T >&& other ) {\n ResourcePointer< T > newPtr;\n newPtr.rawPtr_ = other.rawPtr_;\n other.rawPtr_ = nullptr;\n return newPtr;\n };\n \n ~ResourcePointer() {\n\n // check whether we still own the ressource\n if( rawPtr_ != nullptr ) {\n\n // delete resource, if we are the last one\n int count = ptrCounts_.at( rawPtr_ ) - 1;\n if( count == 0 ) {\n\n // object pointer or array pointer?\n if( std::is_array_v< decltype( rawPtr_ ) > ) {\n delete[] rawPtr_;\n }\n else {\n delete rawPtr_;\n }\n }\n\n // update reference count\n ptrCounts_.at( rawPtr_ ) = count;\n }\n }\n\n int getCount() const {\n return ptrCounts_.at( rawPtr_ );\n }\n\nprivate:\n T* rawPtr_ = nullptr;\n static std::map< T*, int > ptrCounts_;\n};\n\ntemplate< typename T >\nstd::map< T*, int > ResourcePointer<T>::ptrCounts_ = std::map< T*, int >();","metadata":{"trusted":true},"execution_count":18,"outputs":[{"name":"stdout","output_type":"stream","text":"Overwriting ResourcePointer.hpp\n"}],"id":"65826f0b-23af-4577-8e13-ef7c837d84ce"},{"cell_type":"code","source":"!g++ -std=c++17 driver.cpp","metadata":{"trusted":true},"execution_count":19,"outputs":[],"id":"8747be40-d12b-4df8-8ffd-ae636b942c2c"},{"cell_type":"code","source":"!./a.out","metadata":{"trusted":true},"execution_count":20,"outputs":[{"name":"stdout","output_type":"stream","text":"r-value c'tor called!\n"}],"id":"4272df4a-9fcb-49ea-90f4-311f54b6a4eb"},{"cell_type":"markdown","source":"---\nFinally let us try an assignment:","metadata":{},"id":"1490217c-b95c-4981-a4e2-e55f7146d6e9"},{"cell_type":"code","source":"%%file driver.cpp\n\n#include <type_traits>\n#include <iostream>\n#include <map>\n\n#include \"ResourcePointer.hpp\"\n\nint main() {\n ResourcePointer< std::string > stringRes1{ new std::string };\n ResourcePointer< std::string > stringRes2 = new std::string;\n}","metadata":{"trusted":true},"execution_count":21,"outputs":[{"name":"stdout","output_type":"stream","text":"Overwriting driver.cpp\n"}],"id":"7802a7f2-93e5-4ea8-8e6b-e4958e2cec8b"},{"cell_type":"code","source":"!g++ -std=c++17 driver.cpp","metadata":{"trusted":true},"execution_count":22,"outputs":[],"id":"62c7b324-42e4-4097-8cf6-61d1fb06456f"},{"cell_type":"code","source":"!./a.out","metadata":{"trusted":true},"execution_count":23,"outputs":[{"name":"stdout","output_type":"stream","text":"r-value c'tor called!\nr-value c'tor called!\n"}],"id":"381e63e9-64f1-49b7-8685-4df5bd4cefa1"},{"cell_type":"markdown","source":"Hmm, what happened here?","metadata":{},"id":"6ca55ee4-5437-4a54-8c58-9102a0d324a0"},{"cell_type":"markdown","source":"## shared_ptr","metadata":{},"id":"99575f1f-47a9-46cc-8203-51f79fb18ddb"},{"cell_type":"markdown","source":"C++11's `std::shared_ptr` is the professional version of our wrapper class. It is intended to manage the lifetime of a pointer to a shared resource. We again borrow an example from Rainer Grimm's blog to see the basic usage","metadata":{},"id":"eb74d385-b4e8-4f6d-b6da-bea1c1844ce5"},{"cell_type":"code","source":"#include <iostream>\n#include <memory>\n\n// talkative object to make things more verbose\nstruct MyInt {\n MyInt( int v ) : val(v) {\n std::cout << \" Hello: \" << val << std::endl;\n }\n ~MyInt() {\n std::cout << \" Good Bye: \" << val << std::endl;\n }\n int val;\n};","metadata":{"trusted":true},"execution_count":1,"outputs":[],"id":"e1c9d1c4-9cbf-4223-93f7-6fc21ac2a8d9"},{"cell_type":"code","source":"int main() {\n\n std::shared_ptr< MyInt > sharedPtr( new MyInt(1998) );\n\n std::cout << \" My value: \" << sharedPtr->val << std::endl;\n std::cout << \" sharedPtr.use_count(): \" << sharedPtr.use_count() << std::endl;\n\n {\n std::shared_ptr<MyInt> localSharedPtr( sharedPtr );\n std::cout << \" localSharedPtr.use_count(): \" << localSharedPtr.use_count() << std::endl;\n }\n\n std::cout << \" sharedPtr.use_count(): \" << sharedPtr.use_count() << std::endl;\n\n std::shared_ptr<MyInt> globalSharedPtr( sharedPtr );\n std::cout << \" sharedPtr.use_count(): \" << sharedPtr.use_count() << std::endl;\n\n globalSharedPtr.reset();\n std::cout << \" sharedPtr.use_count(): \" << sharedPtr.use_count() << std::endl;\n std::cout << \" globalSharedPtr.use_count(): \" << globalSharedPtr.use_count() << std::endl;\n\n sharedPtr = std::shared_ptr<MyInt>( new MyInt(2011) );\n}","metadata":{"trusted":true},"execution_count":2,"outputs":[],"id":"f3b28d46-77f0-4cfd-ba33-62c248510310"},{"cell_type":"code","source":"main();","metadata":{"trusted":true},"execution_count":3,"outputs":[{"name":"stdout","text":" Hello: 1998\n My value: 1998\n sharedPtr.use_count(): 1\n localSharedPtr.use_count(): 2\n sharedPtr.use_count(): 1\n sharedPtr.use_count(): 2\n sharedPtr.use_count(): 1\n globalSharedPtr.use_count(): 0\n Hello: 2011\n Good Bye: 1998\n Good Bye: 2011\n","output_type":"stream"}],"id":"36f2b915-d40d-49e8-b2bc-b1e7ec1eb424"},{"cell_type":"markdown","source":"Besides its important extra functionality the `std::shared_ptr` behaves like a classic raw pointer:","metadata":{},"id":"566c0d9c-6a28-4bc4-999a-532d1608fb8f"},{"cell_type":"code","source":"#include <iostream>\n\nint main() {\n std::shared_ptr< int > sPtr( new int );\n *sPtr = 2011;\n int a = *sPtr + 12;\n std::cout << \"Going from \" << *sPtr << \" to \" << a << std::endl;\n}\n\nmain();","metadata":{"trusted":true},"execution_count":4,"outputs":[{"name":"stdout","text":"Going from 2011 to 2023\n","output_type":"stream"}],"id":"8723dd29-43e1-4875-bd5d-177002d7b264"},{"cell_type":"markdown","source":"---\nInstead of calling `new` one usually uses `std::make_shared()` to create/initialise a `shared_ptr`.\nFor a discussion of the pros and cons see e.g. the follwing blog entry on\n[Simplify C++](https://arne-mertz.de/2018/09/make_shared-vs-the-normal-shared_ptr-constructor/).","metadata":{},"id":"c0c6a917-d2f5-4aaa-a1df-9e2c6ba6f4d3"},{"cell_type":"code","source":"#include <memory>\n\nint main() {\n\n auto ptr1{ std::make_shared< MyInt >( 2022 ) };\n\n std::shared_ptr< double > ptr2;\n\n // operator bool is overloaded\n if( !ptr2 ) {\n std::cout << \" By default shared_ptr get's initialised to nullptr.\"\n << std::endl;\n }\n\n // can set it later\n ptr2 = std::make_shared< double >();\n\n // and also ask for the underlying memory address\n std::cout << \" Raw address = \" << ptr2.get() << std::endl;\n}\n\nmain();","metadata":{"trusted":true},"execution_count":5,"outputs":[{"name":"stdout","text":" Hello: 2022\n By default shared_ptr get's initialised to nullptr.\n Raw address = 0x560dccf465b0\n Good Bye: 2022\n","output_type":"stream"}],"id":"b253d7bd-ce7a-4bec-b567-e42de97d310d"},{"cell_type":"markdown","source":"## unique_ptr\nThe `std::unique_ptr` syntactically works in the same fashion as its `shared_ptr` brother. However, it is intended to be the single owner of a resource. Thus, it **cannot** be copied. Move semantics, however, will work.","metadata":{},"id":"4af3467e-4535-4347-b44d-e8820539d202"},{"cell_type":"code","source":"int main() {\n\n std::unique_ptr< MyInt > uPtr{ std::make_unique< MyInt >( 42 ) };\n\n // std::unique_ptr< MyInt > ptr2 = uPtr; // illegal\n // std::unique_ptr< MyInt > ptr3{ uPtr }; // illegal\n\n std::cout << \" Raw address (uPtr) = \" << uPtr.get() << std::endl;\n std::unique_ptr< MyInt > ptr2 = std::move( uPtr );\n std::cout << \" Raw address (ptr2) = \" << ptr2.get() << std::endl;\n std::cout << \" Raw address (uPtr) = \" << uPtr.get() << std::endl;\n}\n\nmain();","metadata":{"trusted":true},"execution_count":8,"outputs":[{"name":"stdout","text":" Hello: 42\n Raw address (uPtr) = 0x560dcdc0e0f0\n Raw address (ptr2) = 0x560dcdc0e0f0\n Raw address (uPtr) = 0\n Good Bye: 42\n","output_type":"stream"}],"id":"ff1c233c-e6c3-41eb-8eaf-6f8b694c44b7"},{"cell_type":"markdown","source":"**Note:** <br>\nObjects having `unique_ptr` members can also not be copied as a consequence.","metadata":{},"id":"46445306-615d-4f95-aae9-5019a2ec95e6"},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[],"id":"0304b0a7-e82b-4454-88ed-7205700e3267"}]} \ No newline at end of file -- GitLab