Smart pointers are classes that manage dynamically allocated memory and ensure proper cleanup when the memory is no longer needed. They provide a safer and more convenient way to manage memory than raw pointers.
std::shared_ptr
.Move semantics allow a class to transfer ownership of an object rather than making a copy.
template<typename T>
class Auto_ptr4
{
T* m_ptr {};
public:
Auto_ptr4(T* ptr = nullptr) : m_ptr { ptr } { }
~Auto_ptr4() { delete m_ptr; }
// Copy constructor
Auto_ptr4(const Auto_ptr4& a)
{
m_ptr = new T;
*m_ptr = *a.m_ptr;
}
// Move constructor
Auto_ptr4(Auto_ptr4&& a) noexcept : m_ptr(a.m_ptr)
{
a.m_ptr = nullptr;
}
// Copy assignment
Auto_ptr4& operator=(const Auto_ptr4& a)
{
if (&a == this)
return *this;
delete m_ptr;
m_ptr = new T;
*m_ptr = *a.m_ptr;
return *this;
}
// Move assignment
Auto_ptr4& operator=(Auto_ptr4&& a) noexcept
{
if (&a == this)
return *this;
delete m_ptr;
m_ptr = a.m_ptr;
a.m_ptr = nullptr;
return *this;
}
T& operator*() const { return *m_ptr; }
T* operator->() const { return m_ptr; }
bool isNull() const { return m_ptr == nullptr; }
};
class Resource
{
public:
Resource() { std::cout << "Resource acquired\n"; }
~Resource() { std::cout << "Resource destroyed\n"; }
};
Auto_ptr4<Resource> generateResource()
{
return res; // this return value will invoke the move constructor
}
int main()
{
Auto_ptr4<Resource> mainres;
mainres = generateResource(); // this assignment will invoke the move assignment
return 0;
}
std::unique_ptr
manages a unique resource. It should be used to manage any dynamically allocated object that is not shared by multiple objects.
#include <memory> // for std::unique_ptr
class Resource
{
public:
Resource() { std::cout << "Resource acquired\n"; }
~Resource() { std::cout << "Resource destroyed\n"; }
};
// allocate a Resource object and have it owned by std::unique_ptr
std::unique_ptr<Resource> res{ new Resource() };
// Create a dynamically allocated array of Fractions of length 4
auto f2{ std::make_unique<Fraction[]>(4) };
void takeOwnership(std::unique_ptr<Resource> res)
{
if (res) // use implicit cast to bool to ensure res contains a Resource
{
// do something with res
}
}
int main()
{
std::unique_ptr<Resource> ptr{ new Resource() };
takeOwnership(std::move(ptr)); // use move semantics
return 0;
}
std::unique_ptr
std::array
, std::vector
, or std::string
over a smart pointer managing a fixed array, dynamic array, or C-style string.std::make_unique()
instead of creating std::unique_ptr
and using new
yourself.std::shared_ptr
manages a shared resource using reference counting. The resource will be deallocated when the last std::shared_ptr
managing the resource is destroyed.
#include <memory> // for std::shared_ptr
class Resource
{
public:
Resource() { std::cout << "Resource acquired\n"; }
~Resource() { std::cout << "Resource destroyed\n"; }
};
int main()
{
auto ptr1 { std::make_shared<Resource>() };
auto ptr2 { ptr1 }; // create ptr2 using copy of ptr1
return 0;
}
A std::weak_ptr
is an observer -- it can observe and access the same object as a std::shared_ptr
(or other std::weak_ptr
s) but it is not considered an owner.
#include <memory>
#include <iostream>
class Resource
{
public:
Resource() { std::cout << "Resource acquired\n"; }
~Resource() { std::cout << "Resource destroyed\n"; }
};
std::weak_ptr<Resource> m_partner;
int main()
{
auto ptr1 { std::make_shared<Resource>() };
m_partner = ptr1; // weak_ptr observes the shared_ptr
if (auto shared = m_partner.lock()) // use lock() to convert weak_ptr to shared_ptr
{
std::cout << "Resource is still alive\n";
}
else
{
std::cout << "Resource has been destroyed\n";
}
return 0;
}
Smart pointers provide a safer and more convenient way to manage dynamically allocated memory. Understanding how to use std::unique_ptr
, std::shared_ptr
, and std::weak_ptr
is crucial for writing robust and maintainable C++ code.