diff --git a/notebooks/15_SmartPointers.ipynb b/notebooks/15_SmartPointers.ipynb
index 0b3fc26e5073f343ab9e95d6538f5a67358c572f..7fa84b2fa8afb8f175a9edbdaa62e94233579c5b 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":"code","source":"","metadata":{},"execution_count":null,"outputs":[],"id":"80db8142-9dd8-402e-85df-610b75c9660a"}]}
\ No newline at end of file
+{"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