I wrote a simple reference counted smart pointer. The reference count is supposed to be inside the object it owns (not the smart ptr itself, like std::shared_ptr) and is currently not intended for use with concurrent access in mind. I use C++20 concept to constrain the type so T provides functionality for reference counting.
#ifndef REFCNT_PTR_HPP_INCLUDED
#define REFCNT_PTR_HPP_INCLUDED
#include <memory>
#include <utility>
#include <concepts>
namespace refcnt {
template <typename T>
concept ref_counted = requires(T* obj) {
{ obj->add_ref() } -> std::same_as<void>;
{ obj->release() } -> std::same_as<int>;
};
template <ref_counted T, typename Deleter = std::default_delete<T>>
class refcnt_ptr {
void release() {
if(ptr && ptr->release() == 0) {
deleter(ptr);
}
}
T* ptr;
Deleter deleter;
public:
refcnt_ptr() noexcept: ptr(nullptr), deleter({}) { }
refcnt_ptr(T* ptr, Deleter deleter = {}) noexcept
: ptr(ptr), deleter(deleter) {
if(ptr) {
ptr->add_ref();
}
}
refcnt_ptr(const refcnt_ptr& other) noexcept : ptr(other.ptr), deleter(other.deleter) {
if(ptr) {
ptr->add_ref();
}
}
refcnt_ptr(refcnt_ptr&& other) noexcept : ptr(other.ptr), deleter(std::move(other.deleter)) {
other.ptr = nullptr;
}
~refcnt_ptr() {
release();
}
refcnt_ptr& operator=(const refcnt_ptr& other) noexcept {
if(this != &other) {
release();
ptr = other.ptr;
deleter = other.deleter;
if(ptr) {
ptr->add_ref();
}
}
return *this;
}
refcnt_ptr& operator=(refcnt_ptr&& other) noexcept {
if(this != &other) {
release();
ptr = other.ptr;
deleter = std::move(other.deleter);
other.ptr = nullptr;
}
return *this;
}
T* get() const noexcept {
return ptr;
}
T& operator*() const noexcept {
return *ptr;
}
T* operator->() const noexcept {
return ptr;
}
explicit operator bool() const noexcept {
return ptr != nullptr;
}
};
} // namespace refcnt
#endif
Simple test program:
#include "refcnt_ptr.hpp"
#include <cstdio>
#include <memory>
struct test {
int ref;
void add_ref() {
printf("add_ref\n");
++ref;
}
int release() {
printf("release\n");
return --ref;
}
test& operator=(const test&) = delete;
test& operator=(test&&) = delete;
test() = default;
test(const test&) = delete;
test(test&&) = delete;
~test() {
printf("Delete\n");
}
};
void func2(refcnt::refcnt_ptr<test> ptr) {
}
void func(refcnt::refcnt_ptr<test> ptr) {
func2(ptr);
}
int main() {
auto smart = refcnt::refcnt_ptr<test>(new test());
func(smart);
func(smart);
}