Luciano Muratore

Implementing std::unique_ptr from Scratch

June 17, 2026 · Luciano Muratore

Unique Ownership

A Unique_ptr is a Smart Pointer with the peculiarity that it assigns unique ownership over a managed object.

The present article shows an implementation of unique_ptr, showing the representation of the move constructor, the move assignment, the destructor and also the constructor.

The fact that this pointer is the only one that owns a managed object means that it is not possible to do pointer copy. Having said that, the copy constructor and the copy assignment need to be deleted.

//Copy Constructor Deleted
unique_ptr(const unique_ptr &)=delete;
        
//Copy Assignment Deleted
unique_ptr &operator=(const unique_ptr &)=delete;

Transfer of Ownership

As said before, since there is only one smart pointer with ownership over the X managed object, then we need to define a move constructor and a move assignment so that a source raw pointer passes its ownership into a other raw pointer. Before going to them, we need to go through release and reset methods.

//Release Method
T *release() noexcept {return std::exchange(m_ptr,nullptr);}

std::exchange assigns the new value (nullptr) to m_ptr, and it returns the previous value of m_ptr.

//Reset Method
void reset(T *ptr = nullptr) noexcept {
    T *old = std::exchange(m_ptr, ptr);
    if (old) {
        delete old;
    }
}

reset() is a safety method because it guarantees that the old source raw pointer is deleted.

Now, let’s move into the Move Constructor and the Move Assignment.

//move constructor
unique_ptr(unique_ptr &&other) noexcept : m_ptr{other.release()} {}

//move assignment
unique_ptr &operator=(unique_ptr &&other) noexcept {
    if (this != &other) {
        reset(other.release());
    }
    return *this;
}

if(this != & other) is a self-assignment guard. It ensures that you accidentally write p1=std::move(p1), you don’t accidentally delete your own pointer before trying to assign it.

Destructor

Since the unique_ptr is a smart pointer, it needs to be deleted via the destructor

//Destructor
~unique_ptr() noexcept {
    if (m_ptr) {
        delete m_ptr;
    }
}

Code Implementation

#include <iostream>

template<class T>
class unique_ptr{
    private:
        T* m_ptr;

    public:
        //Constructor
        unique_ptr() noexcept : unique_ptr{nullptr} {}
        explicit unique_ptr(T* ptr) noexcept : m_ptr{ptr} {}

        //Copy Constructor Deleted
        unique_ptr(const unique_ptr &)=delete;
        
        //Copy Assignment Deleted
        unique_ptr &operator=(const unique_ptr &)=delete;

        

        //move constructor
        unique_ptr(unique_ptr &&other) noexcept : m_ptr{other.release()} {}

        //move assignment
        unique_ptr &operator=(unique_ptr &&other) noexcept {
            if (this != &other) {
                reset(other.release());
            }
            return *this;
        }

        //Reset Method
        void reset(T *ptr = nullptr) noexcept {
            T *old = std::exchange(m_ptr, ptr);
            if (old) {
                delete old;
            }
        }

        //Get Method
        T *get() const noexcept { return m_ptr; }
        
        //Pointer Operators
        T *operator->() const noexcept { return m_ptr; }
        T &operator*() const noexcept { return *m_ptr; }

        //Destructor
        ~unique_ptr() noexcept {
            if (m_ptr) {
                delete m_ptr;
            }
        }
};

template <class T, class... Args>
unique_ptr<T> make_unique(Args &&...args) {
    return unique_ptr<T>(new T(std::forward<Args>(args)...));
}