Skip to content

Smart pointers

[std::unique_ptr] – [std::shared_ptr] – [std::weak_ptr] – [Example] – [Share parts of a managed object] –

For more info about smart pointers see also http://www.gerald-fahrnholz.eu/sw/display_contents.php?file_name=c11_smart_ptr.htm

std::unique_ptr

  • resource is owned by single owner
  • pointer cannot be shared with other clients
  • unique_ptr has no copy constructor (pass as reference or use std::move to give access or use a shared_ptr)

std::shared_ptr

  • resource may be owned by multiple clients
  • when shared_ptr is copied an internal use count is incremented
  • object is destroyed when the last client has released its shared_pointer (i.e. the use count is incremented to 0)

std::weak_ptr

  • works together with a shared_ptr and its internal control block (contains the use count and the number of connected weak pointers)
  • does not hold the object alive (i.e. has no use count)
  • before accessing the object through a weak_ptr first try to get a valid shared_ptr (which gets its own use count) but it is possible that the object has already been deleted
  • used to avoid circular references where objects A and B both hold shared_ptr instances of each other and none of them will ever be destroyed

Example

Data class with all types of shared pointers:

struct MyData
{
    int num{};
    double doubleVal{};
};

using MyDataUp = std::unique_ptr<MyData>;
using MyDataSp = std::shared_ptr<MyData>;
using MyDataWp = std::weak_ptr<MyData>;

Using the smart pointers:

    // Pass any arguments for constructing the object
    auto upData1 = std::make_unique<MyData>(42);
    auto upData2 = std::make_unique<MyData>(4711, 3.14);
    
    auto spData = std::make_shared<MyData>(4712, 3.15);

    MyDataWp wpData = spData;
    if (auto spAccess = wpData.lock()) // check if object is still available
    {
        std::cout << spAccess->doubleVal << '\n';
    }

Share parts of a managed object

Assume you want to share a part of a bigger object through a shared_ptr. Of course you do not want that the part is still used by some client while the object has already been destroyed. You can make this work by returning a shared_ptr to the part which uses the regular use count of the whole object:

    auto spWholeData = std::make_shared<MyData>(44, 8.77);
    // aliasing constructor of shared_ptr:
    auto spDoubleVal = std::shared_ptr<double>(spWholeData, &spWholeData->doubleVal);
    spWholeData.reset();
    // whole object is still alive and access to data member is still possible
    std::cout << *spDoubleVal << '\n';
    spDoubleVal.reset(); // whole object gets destroyed