Below is the entire code. I appreciate if, someone with more C++ experience can suggest if this can be improved further and if you notice any issues.
controlblock.hpp
template<class T>
struct ControlBlock
{
int _ref_count;
int _weak_count;
void inc_ref() noexcept { ++_ref_count; }
void inc_wref() noexcept { ++_weak_count; }
void dec_ref() noexcept { --_ref_count; }
void dec_wref() noexcept { if (--_weak_count == 0) delete this; }
int use_count() const noexcept { return _ref_count; }
ControlBlock() : _ref_count{ 0 }, _weak_count{ 0 } {}
~ControlBlock() { _ref_count = 0; _weak_count = 0; }
};
shared_ptr.hpp
#pragma once
#include <algorithm>
#include <iterator>
#include <compare>
#include <cassert>
#include <memory>
#include "controlblock.hpp"
#include "weak_ptr.hpp"
template<class T>
class SharedPtr {
#define CHECK assert(Invariant());
template<class Y> friend class WeakPtr;
private:
T* _ptr;
ControlBlock<T>* _ctrl_block;
void __increment_reference()
{
if (_ptr != nullptr && _ctrl_block != nullptr)
_ctrl_block->inc_ref();
}
void __remove_reference()
{
if (_ctrl_block && _ctrl_block->_ref_count > 0) {
--_ctrl_block->_ref_count;
if(_ctrl_block && _ctrl_block->_ref_count == 0){
delete _ptr;
}
if(_ctrl_block->_ref_count + _ctrl_block->_weak_count == 0) {
delete _ctrl_block;
}
_ptr = nullptr;
_ctrl_block = nullptr;
}
}
public:
constexpr SharedPtr() : _ptr{ nullptr }, _ctrl_block{ nullptr } { }
explicit SharedPtr(T* other) : _ptr{ other }, _ctrl_block{new ControlBlock<T>}
{
__increment_reference();
CHECK
}
constexpr SharedPtr(const std::nullptr_t) noexcept
: _ptr{ nullptr }, _ctrl_block{ nullptr } { CHECK }
explicit SharedPtr(WeakPtr<T>& other)
: _ptr{other._ptr}, _ctrl_block{other._ctrl_block}
{
if (other.expired())
throw std::bad_weak_ptr();
_ctrl_block->inc_wref();
CHECK
}
SharedPtr(const SharedPtr& other) noexcept
: _ptr{other._ptr}, _ctrl_block{other._ctrl_block}
{
__increment_reference();
CHECK
}
SharedPtr(SharedPtr&& other) noexcept
: _ptr{ std::exchange(other._ptr, nullptr) },
_ctrl_block{std::exchange(other._ctrl_block, nullptr)}
{
CHECK
}
~SharedPtr() { __remove_reference(); CHECK };
SharedPtr& operator=(const SharedPtr& other) noexcept
{
if (this == &other)
return *this;
__remove_reference();
_ptr = other._ptr;
_ctrl_block = other._ctrl_block;
__increment_reference();
CHECK
return *this;
}
SharedPtr& operator=(SharedPtr&& other) noexcept
{
if (this == &other)
return *this;
__remove_reference();
_ptr = std::exchange(other._ptr, nullptr);
_ctrl_block = std::exchange(other._ctrl_block, nullptr);
CHECK
return *this;
}
SharedPtr& operator=(std::nullptr_t)
{
if (_ptr == nullptr && _ctrl_block == nullptr)
return *this;
__remove_reference();
return *this;
}
T* get() const noexcept { return _ptr; }
T& operator*() const noexcept { return *_ptr; }
T* operator->() const noexcept { return _ptr; }
operator bool() const noexcept { return _ptr != nullptr && _ctrl_block != nullptr; }
size_t use_count() const noexcept { return (_ctrl_block) ? _ctrl_block->use_count() : 0; }
void reset() noexcept { this->__remove_reference(); }
bool Invariant() const noexcept;
template<class T> friend void swap(SharedPtr<T>& lhs, SharedPtr<T>& rhs) noexcept;
friend auto operator<=>(const SharedPtr& lhs, const SharedPtr& rhs) = default;
friend auto operator==(const SharedPtr& lhs, const SharedPtr& rhs)
{
if (lhs.get() != rhs.get())
return false;
return (lhs.get() <=> rhs.get()) == 0;
}
};
template<class T>
inline bool SharedPtr<T>::Invariant() const noexcept
{
if (_ptr == nullptr && _ctrl_block == nullptr)
return true;
else if (_ptr != nullptr || _ctrl_block != nullptr &&
_ctrl_block->_ref_count > 0 || _ctrl_block->_weak_count > 0)
return true;
return false;
}
template<class T> void swap(SharedPtr<T>& lhs, SharedPtr<T>& rhs) noexcept
{
std::swap(lhs, rhs);
}
template<class T, class ...Args>
SharedPtr<T> MakeShared(Args && ...args)
{
return SharedPtr<T>(new T(std::forward<Args>(args)...));
}
weak_ptr.hpp
#pragma once
#include "controlblock.hpp"
#include "shared_ptr.hpp"
template<class T>
class WeakPtr {
#define CHECK assert(Invariant());
template<class Y> friend class SharedPtr;
private:
T* _ptr;
ControlBlock<T>* _ctrl_block;
public:
WeakPtr() { CHECK }
WeakPtr(T* other) : _ptr{other}, _ctrl_block{new ControlBlock<T>()} { __increment_weakptr(); CHECK }
WeakPtr(const WeakPtr& other) noexcept : _ptr{other._ptr}, _ctrl_block{other._ctrl_block}
{
__increment_weakptr();
CHECK
}
WeakPtr(const SharedPtr<T>& other) noexcept
: _ptr{other._ptr}, _ctrl_block{other._ctrl_block}
{
if(_ptr != nullptr && _ctrl_block != nullptr)
_ctrl_block->inc_wref();
CHECK
}
~WeakPtr() { __decrement_weakptr(); CHECK }
void reset() noexcept
{
this->__decrement_weakptr();
}
bool expired() noexcept
{
if (_ctrl_block == nullptr)
return true;
if (_ctrl_block->_ref_count == 0)
--_ctrl_block->_weak_count;
if (_ctrl_block->_ref_count + _ctrl_block->_weak_count == 0) {
delete _ctrl_block;
_ptr = nullptr;
_ctrl_block = nullptr;
}
return !_ctrl_block || _ctrl_block->_weak_count == 0;
}
auto lock()
{
return expired() ? SharedPtr<T>() : SharedPtr<T>(*this);
}
bool Invariant() const noexcept
{
if (_ptr == nullptr)
return true;
return _ctrl_block->_weak_count > 0;
}
template<class T>
friend void swap(WeakPtr<T>& lhs, WeakPtr <T>& rhs) noexcept;
WeakPtr& operator=(const WeakPtr& other)
{
if (this != &other) {
__decrement_weakptr();
_ptr = other._ptr;
_ctrl_block = other._ctrl_block;
__increment_weakptr();
}
CHECK
return *this;
}
WeakPtr& operator=(const SharedPtr<T>& other) {
_ptr = other._ptr;
_ctrl_block = other._ctrl_block;
if(_ptr != nullptr && _ctrl_block != nullptr)
_ctrl_block->inc_wref();
CHECK
return *this;
}
private:
void __increment_weakptr()
{
if (_ctrl_block != nullptr) {
_ctrl_block->inc_wref();
}
}
void __decrement_weakptr()
{
if (_ctrl_block != nullptr && _ptr != nullptr) {
if (--_ctrl_block->_weak_count == 0 && _ctrl_block->_ref_count == 0) {
delete _ctrl_block;
delete _ptr;
}
}
_ptr = nullptr;
_ctrl_block = nullptr;
}
};
template<class T>
inline void swap(WeakPtr<T>& lhs, WeakPtr<T>& rhs) noexcept
{
std::swap(lhs, rhs);
}