I have homework in college to implement intrusive smart pointers.
#include <atomic>
#include <string_view>
#include <string>
#include <iostream>
struct RefCount
{
inline void IncrementRefCount() const noexcept
{
refCount.fetch_add(1, std::memory_order_relaxed);
}
inline void DecrementRefCount() const noexcept
{
refCount.fetch_sub(1, std::memory_order_acq_rel);
}
[[nodiscard]] inline uint32_t getRefCount() const noexcept { return refCount; }
private:
mutable std::atomic_uint32_t refCount = 0;
};
template<typename T>
struct Ref
{
Ref() = default;
Ref(std::nullptr_t) noexcept
: data(nullptr)
{
}
Ref(T* instance) noexcept
: data(instance)
{
IncrementRef();
}
template<typename T2>
Ref(const Ref<T2>& other) noexcept
{
data = (T*)other.data;
IncrementRef();
}
template<typename T2>
Ref(Ref<T2>&& other) noexcept
{
data = (T*)other.data;
other.data = nullptr;
}
~Ref() noexcept
{
DecrementRef();
}
Ref(const Ref<T>& other) noexcept
: data(other.data)
{
IncrementRef();
}
[[nodiscard]] inline Ref& operator=(std::nullptr_t) noexcept
{
DecrementRef();
data = nullptr;
return *this;
}
[[nodiscard]] inline Ref& operator=(const Ref<T>& other) noexcept
{
other.IncrementRef();
DecrementRef();
data = other.data;
return *this;
}
template<typename T2>
[[nodiscard]] inline Ref& operator=(const Ref<T2>& other) noexcept
{
other.IncrementRef();
DecrementRef();
data = other.data;
return *this;
}
template<typename T2>
[[nodiscard]] inline Ref& operator=(Ref<T2>&& other) noexcept
{
DecrementRef();
data = other.data;
other.data = nullptr;
return *this;
}
[[nodiscard]] inline operator bool() noexcept { return data != nullptr; }
[[nodiscard]] inline operator bool() const noexcept { return data != nullptr; }
[[nodiscard]] inline bool operator==(const Ref<T>& other) const noexcept
{
return data == other.data;
}
[[nodiscard]] inline bool operator!=(const Ref<T>& other) const noexcept
{
return !(*this == other);
}
[[nodiscard]] inline T* operator->() noexcept { return data; }
[[nodiscard]] inline const T* operator->() const noexcept { return data; }
[[nodiscard]] inline T& operator*() noexcept { return *data; }
[[nodiscard]] inline const T& operator*() const noexcept { return *data; }
[[nodiscard]] inline T* get() noexcept { return data; }
[[nodiscard]] inline const T* get() const noexcept { return data; }
inline void Reset(T* instance = nullptr) noexcept
{
DecrementRef();
data = instance;
}
template<typename T2>
[[nodiscard]] inline Ref<T2> as() const noexcept
{
return Ref<T2>(*this);
}
template<typename... Args>
[[nodiscard]] inline static Ref<T> Create(Args&&... args) noexcept
{
return Ref<T>(new T(std::forward<Args>(args)...));
}
private:
inline void IncrementRef() const noexcept
{
if (data) {
data->IncrementRefCount();
}
}
inline void DecrementRef() const noexcept
{
if (data) {
data->DecrementRefCount();
if (data->getRefCount() == 0) {
delete data;
}
}
}
template<typename T2>
friend struct Ref;
T* data = nullptr;
};
template<typename T>
class WeakRef
{
public:
WeakRef() noexcept = default;
WeakRef(Ref<T> ref) noexcept
{
data = ref.get();
}
WeakRef(T* instance) noexcept
: data(instance)
{
}
[[nodiscard]] inline Ref<T> lock() const noexcept { return Ref<T>(data); }
[[nodiscard]] inline bool isValid() const noexcept { return data; }
[[nodiscard]] inline operator bool() const noexcept { return isValid(); }
private:
T* data = nullptr;
};
struct Leaf;
struct Node : RefCount
{
virtual ~Node() noexcept = default;
void PrintLeafName() noexcept const;
Ref<Leaf> leaf = nullptr;
std::string name;
protected:
Node(const std::string_view name) noexcept
: name(name)
{
}
};
struct ConcreteNode : Node
{
ConcreteNode(const std::string_view name, const std::string_view leafName) noexcept;
~ConcreteNode() noexcept override = default;
};
struct Leaf : RefCount
{
virtual ~Leaf() noexcept = default;
virtual void PrintName() const noexcept = 0;
void PrintNodeName() const noexcept
{
std::cout << parent.lock()->name << "\n";
}
protected:
Leaf(WeakRef<Node> parent, const std::string_view name) noexcept
: parent(std::move(parent)), name(name)
{
}
WeakRef<Node> parent;
std::string name;
};
struct ConcreteLeaf : Leaf
{
ConcreteLeaf(WeakRef<Node> parent, const std::string_view name) noexcept
: Leaf(std::move(parent), name)
{
}
~ConcreteLeaf() noexcept override = default;
void PrintName() const noexcept
{
std::cout << name <<"\n";
}
};
ConcreteNode::ConcreteNode(std::string_view name, std::string_view leafName) noexcept
: Node(name)
{
leaf = Ref<ConcreteLeaf>::Create(this, leafName);
}
void Node::PrintLeafName() const noexcept
{
leaf->PrintName();
}
int main()
{
Ref<Node> node = Ref<ConcreteNode>::Create("Concrete node", "Concrete leaf");
node->PrintLeafName();
node->leaf->PrintNodeName();
return 0;
}
Could you review it please? I'm not sure if RefCount class needs a virtual destructor. Ref class deletes T, so I don't think it's needed, but I may be wrong.
I'm also not sure of mutable std::atomic_uint32_t refCount, but it's required for RefCount::as() const