All Notes

Implementing STL's std::shared_ptr

Last updated

Nov 04, 2025

my own implementation of shared ptr and it’s corresponding weak ptrs

not ready to do a write up on it. i want to see other people’s implementation - i think control block doesn’t need to be its own component in a different file - just define it in sharedptr class and make it a friend?

shared ptr

#pragma once

#include <utility>
#include <memory>
#include <atomic>

#include "utils/ControlBlock.h"

template<typename T, typename Deleter = std::default_delete<T>>
class SharedPtr {
public:
/* Constructors */
SharedPtr() noexcept = default;

SharedPtr(T* ptr) noexcept : ptr_(ptr) {
if (ptr) {
control_block_ = new ControlBlock<T, Deleter>(Deleter{});
} else {
control_block_ = nullptr;
}
}

SharedPtr(T* ptr, Deleter deleter) noexcept : ptr_(ptr) {
if (ptr) {
control_block_ = new ControlBlock<T, Deleter>(deleter);
} else {
control_block_ = nullptr;
}
}

SharedPtr(const SharedPtr& other) noexcept : ptr_(other.ptr_), control_block_(other.control_block_) {
if (control_block_) {
control_block_->ref_count_.fetch_add(1);
}
}

SharedPtr(SharedPtr&& other) noexcept :
ptr_(std::exchange(other.ptr_, nullptr)),
control_block_(std::exchange(other.control_block_, nullptr)) {}

/* Destructor */
~SharedPtr() noexcept {
if (control_block_ && control_block_->ref_count_.fetch_sub(1) == 1) {
control_block_->deleter_(ptr_);

if (control_block_->weak_count_.load() == 0) delete control_block_;
}
}

/* Assignment operators */
SharedPtr& operator=(SharedPtr&& other) noexcept {
if (this != &other) {
if (control_block_ && control_block_->ref_count_.fetch_sub(1) == 1) {
control_block_->deleter_(ptr_);
delete control_block_;
}

ptr_ = std::exchange(other.ptr_, nullptr);
control_block_ = std::exchange(other.control_block_, nullptr);
}

return *this;
}

SharedPtr& operator=(const SharedPtr& other) noexcept {
if (this != &other) {
if (control_block_ && control_block_->ref_count_.fetch_sub(1) == 1) {
control_block_->deleter_(ptr_);
delete control_block_;
}

ptr_ = other.ptr_;
control_block_ = other.control_block_;

if (control_block_) control_block_->ref_count_++;
}

return *this;
}

T* get() const noexcept {
return ptr_;
}

T* operator->() const noexcept {
return ptr_;
}

T& operator*() const noexcept {
return *ptr_;
}

long use_count() const noexcept {
if (control_block_) {
return control_block_->ref_count_.load();
}

return 0;
}

bool unique() const noexcept {
return use_count() == 1;
}

void reset(T* ptr = nullptr) noexcept {
if (control_block_ && control_block_->ref_count_.fetch_sub(1) == 1) {
control_block_->deleter_(ptr_);
delete control_block_;
}

ptr_ = ptr;
if (ptr) {
control_block_ = new ControlBlock<T, Deleter>(Deleter{});
} else {
control_block_ = nullptr;
}
}

void swap(SharedPtr& other) noexcept {
std::swap(ptr_, other.ptr_);
std::swap(control_block_, other.control_block_);
}

explicit operator bool() const noexcept {
return ptr_ != nullptr;
}

bool operator==(const SharedPtr& other) const noexcept {
return ptr_ == other.ptr_;
}

bool operator!=(const SharedPtr& other) const noexcept {
return !(ptr_ == other.ptr_);
}

bool operator==(std::nullptr_t) const noexcept {
return ptr_ == nullptr;
}

bool operator!=(std::nullptr_t) const noexcept {
return !(ptr_ == nullptr);
}

private:
SharedPtr(T* ptr, ControlBlock<T, Deleter>* control_block) :
ptr_(ptr), control_block_(control_block) {}

// WeakPtr should be able to access the private ctor
// WeakPtr here is a forward declaration - no need to import module explicitly
template<typename U> friend class WeakPtr;

private:
T* ptr_ = nullptr;
ControlBlock<T, Deleter>* control_block_ = nullptr;
};

// TODO:
// make_shared exists but we're not doing the performance improvements
// to improve cache locality / reduce cache misses
template<typename T, typename... Arg>
SharedPtr<T> make_shared(Arg&&... args) {
return SharedPtr<T>(new T(std::forward<Arg>(args)...));
}

weak ptr

#pragma once

#include <utility>
#include "utils/ControlBlock.h"
#include "shared_ptr/SharedPtr.h"

namespace {
template<typename T>
void try_delete_control_block(ControlBlock<T>* control_block) {
if (control_block && control_block->weak_count_.fetch_sub(1) == 1) {
if (control_block->ref_count_.load() == 0) {
delete control_block;
}
}
}
}

template<typename T>
class WeakPtr {
public:
WeakPtr() = default;

WeakPtr(const SharedPtr<T>& shared_ptr) noexcept :
ptr_(shared_ptr.ptr_),
control_block_(shared_ptr.control_block_) {

if (control_block_) control_block_->weak_count_++;
}

WeakPtr(const WeakPtr& other) noexcept :
ptr_(other.ptr_),
control_block_(other.control_block_) {
if (control_block_) control_block_->weak_count_++;
}

WeakPtr(WeakPtr&& other) noexcept :
ptr_(std::exchange(other.ptr_, nullptr)),
control_block_(std::exchange(other.control_block_, nullptr)) {}

~WeakPtr() noexcept {
try_delete_control_block(control_block_);
}

// operator assign {copy, move}
WeakPtr& operator=(const WeakPtr& other) noexcept {
if (this != &other) {
try_delete_control_block(control_block_);

ptr_ = other.ptr_;
control_block_ = other.control_block_;

if (control_block_) control_block_->weak_count_++;
}

return *this;
}

WeakPtr& operator=(WeakPtr&& other) noexcept {
if (this != &other) {
try_delete_control_block(control_block_);
ptr_ = std::exchange(other.ptr_, nullptr);
control_block_ = std::exchange(other.control_block_, nullptr);
}

return *this;
}

bool expired() const noexcept {
if (!control_block_) return true;
return control_block_->ref_count_.load() == 0;
}

long use_count() const noexcept {
if (!control_block_) return 0;

return control_block_->ref_count_.load();
}

SharedPtr<T> lock() const noexcept {
if (!control_block_) {
return SharedPtr<T>();
}

auto ref_count = control_block_->ref_count_.load();
do {
if (ref_count == 0) {
return SharedPtr<T>();
}
} while(!control_block_->ref_count_.compare_exchange_weak(ref_count, ref_count + 1));

return SharedPtr<T>(ptr_, control_block_);
}

void reset() noexcept {
ptr_ = nullptr;
try_delete_control_block(control_block_);
control_block_ = nullptr;
}

void swap(WeakPtr& other) noexcept {
std::swap(ptr_, other.ptr_);
std::swap(control_block_, other.control_block_);
}

private:
T* ptr_ = nullptr;
ControlBlock<T>* control_block_ = nullptr;
};

ctrl block

#pragma once

#include <memory>

template<typename T, typename Deleter = std::default_delete<T>>
struct ControlBlock {
ControlBlock(Deleter deleter) :
ref_count_(1),
weak_count_(0),
deleter_(deleter) {}

std::atomic<long> ref_count_;
std::atomic<long> weak_count_;
Deleter deleter_{};
};

Other notes about STL Implementations and/or C++