#include #include struct StandardDelete { template void operator()(T* ptr) { delete ptr; } }; struct Deleter { virtual void destroy(void*) = 0; virtual void deallocate(void*) = 0; virtual ~Deleter() {} }; template struct ObjectManagerBlock : Deleter { Allocator allocator_; ObjectManagerBlock(const Allocator& allocator) : allocator_(allocator) {} void destroy(void* ptr) override { using ConstructAllocator = typename std::allocator_traits::template rebind_alloc; using ConstructAllocatorTraits = typename std::allocator_traits; ConstructAllocator construct_allocator = allocator_; ConstructAllocatorTraits::destroy(construct_allocator, static_cast(ptr)); } void deallocate(void* ptr) override { using MemoryAllocator = typename std::allocator_traits::template rebind_alloc; using MemoryAllocatorTraits = typename std::allocator_traits; MemoryAllocator memory_allocator = allocator_; MemoryAllocatorTraits::deallocate(memory_allocator, reinterpret_cast(ptr), sizeof(T) + 2 * sizeof(size_t) + sizeof(Deleter)); } }; template struct DeleterWithSpecialDeleter : Deleter { Allocator allocator_; TDeleter t_deleter_; DeleterWithSpecialDeleter(const Allocator& allocator, const TDeleter& t_deleter) : allocator_(allocator), t_deleter_(t_deleter) {} void destroy(void* ptr) override { t_deleter_(reinterpret_cast(ptr)); } void deallocate(void* ptr) override { using MemoryAllocator = typename std::allocator_traits::template rebind_alloc; using MemoryAllocatorTraits = typename std::allocator_traits; MemoryAllocator memory_allocator = allocator_; MemoryAllocatorTraits::deallocate(memory_allocator, reinterpret_cast(ptr), 2 * sizeof(size_t) + sizeof(Deleter)); } }; template class WeakPtr; template class SharedPtr { private: template friend class SharedPtr; template friend class WeakPtr; struct TemplateConstructorTag {}; void decrease_counter() { if (counter_) { --*counter_; if (*counter_ == 0) { deleter_->destroy(value_); if (*weak_counter_ == 0) { if (!is_block_) { deleter_->deallocate(counter_); } else { deleter_->deallocate(value_); } } } } } void increase_counter() { if (counter_) { ++*counter_; } } void reset_without_decrease() { value_ = nullptr; counter_ = nullptr; weak_counter_ = nullptr; deleter_ = nullptr; is_block_ = false; } public: ~SharedPtr() { decrease_counter(); } SharedPtr() {} SharedPtr(T* ptr) : SharedPtr(ptr, StandardDelete(), std::allocator()) {} template> SharedPtr(T* ptr, const TDeleter& deleter, Allocator allocator = Allocator()) : SharedPtr() { reset(ptr, allocator, deleter); } template, class TDeleter = StandardDelete> void reset(T* ptr, Allocator allocator = Allocator(), const TDeleter& t_deleter = TDeleter()) { decrease_counter(); value_ = ptr; is_block_ = false; using MemoryAllocator = typename std::allocator_traits::template rebind_alloc; using MemoryAllocatorTraits = typename std::allocator_traits; MemoryAllocator memory_allocator = allocator; auto mem = MemoryAllocatorTraits::allocate(memory_allocator, 2 * sizeof(size_t) + sizeof(Deleter)); counter_ = reinterpret_cast(mem); *counter_ = 1; weak_counter_ = reinterpret_cast(mem + sizeof(size_t)); *weak_counter_ = 0; deleter_ = reinterpret_cast(mem + 2 * sizeof(size_t)); new(deleter_) DeleterWithSpecialDeleter(allocator, t_deleter); } void reset() { decrease_counter(); reset_without_decrease(); } SharedPtr(SharedPtr&& another) : SharedPtr(std::move(another), TemplateConstructorTag()) {} template SharedPtr(SharedPtr&& another, TemplateConstructorTag = TemplateConstructorTag()) { value_ = std::move(another.value_); counter_ = std::move(another.counter_); weak_counter_ = std::move(another.weak_counter_); deleter_ = std::move(another.deleter_); is_block_ = another.is_block_; another.reset_without_decrease(); } SharedPtr(const SharedPtr& another) : SharedPtr(another, TemplateConstructorTag()) {} template SharedPtr(const SharedPtr& another, TemplateConstructorTag = TemplateConstructorTag()) { value_ = another.value_; counter_ = another.counter_; weak_counter_ = another.weak_counter_; deleter_ = another.deleter_; is_block_ = another.is_block_; increase_counter(); } SharedPtr& operator=(const SharedPtr& another) { return operator=(another); } template SharedPtr& operator=(const SharedPtr& another) { SharedPtr tmp(another); swap(tmp); return *this; } SharedPtr& operator=(SharedPtr&& another) { return operator=(std::move(another)); } template SharedPtr& operator=(SharedPtr&& another) { SharedPtr tmp(std::move(another)); swap(tmp); return *this; } size_t use_count() const { return *counter_; } T* operator->() { return value_; } T& operator*() { return *value_; } const T& operator*() const { return *value_; } T* get() { return value_; } const T* get() const { return value_; } void swap(SharedPtr& another) { std::swap(value_, another.value_); std::swap(counter_, another.counter_); std::swap(weak_counter_, another.weak_counter_); std::swap(deleter_, another.deleter_); std::swap(is_block_, another.is_block_); } private: template friend SharedPtr makeShared(Args&& ... args); template friend SharedPtr allocateShared(const Alloc& alloc, Args&& ... args); friend WeakPtr; T* value_ = nullptr; size_t* counter_ = nullptr; size_t* weak_counter_ = nullptr; Deleter* deleter_ = nullptr; bool is_block_ = false; template> SharedPtr(T* ptr, size_t* counter, size_t* weak_counter, Deleter* deleter, const Alloc& alloc) : value_(ptr), counter_(counter), weak_counter_(weak_counter), deleter_(deleter), is_block_(true) { increase_counter(); new(deleter) ObjectManagerBlock(alloc); } SharedPtr(T* ptr, size_t* counter, size_t* weak_counter, Deleter* deleter, bool is_block) : value_(ptr), counter_(counter), weak_counter_(weak_counter), deleter_(deleter), is_block_(is_block) { increase_counter(); } }; template class WeakPtr { template friend class WeakPtr; struct TemplateConstructorTag {}; public: ~WeakPtr() { decrease_counter(); } WeakPtr() {} WeakPtr(const SharedPtr& another) : WeakPtr(another, TemplateConstructorTag()) {} template WeakPtr(const SharedPtr& another, TemplateConstructorTag = TemplateConstructorTag()) { value_ = another.value_; counter_ = another.counter_; weak_counter_ = another.weak_counter_; deleter_ = another.deleter_; is_block_ = another.is_block_; increase_counter(); } WeakPtr(const WeakPtr& another) : WeakPtr(another, TemplateConstructorTag()) {} template WeakPtr(const WeakPtr& another, TemplateConstructorTag = TemplateConstructorTag()) { value_ = another.value_; counter_ = another.counter_; weak_counter_ = another.weak_counter_; deleter_ = another.deleter_; is_block_ = another.is_block_; increase_counter(); } size_t use_count() const { return *counter_; } bool expired() const { return *counter_ == 0; } SharedPtr lock() const { return SharedPtr(value_, counter_, weak_counter_, deleter_, is_block_); } WeakPtr& operator=(const WeakPtr& another) { WeakPtr tmp(another); swap(tmp); return *this; } WeakPtr& operator=(WeakPtr&& another) { WeakPtr tmp(another); swap(tmp); return *this; } WeakPtr& operator=(const SharedPtr& another) { WeakPtr tmp(another); swap(tmp); return *this; } private: void decrease_counter() { if (weak_counter_) { --*weak_counter_; if (*counter_ == 0 && *weak_counter_ == 0) { if (!is_block_) { deleter_->deallocate(counter_); } else { deleter_->deallocate(value_); } } } } void increase_counter() { if (weak_counter_) { ++*weak_counter_; } } void swap(WeakPtr& another) { std::swap(value_, another.value_); std::swap(counter_, another.counter_); std::swap(weak_counter_, another.weak_counter_); std::swap(deleter_, another.deleter_); } T* value_ = nullptr; size_t* counter_ = nullptr; size_t* weak_counter_ = nullptr; Deleter* deleter_ = nullptr; bool is_block_ = false; }; template SharedPtr allocateShared(const Alloc& alloc, Args&& ... args) { using MemoryAllocator = typename std::allocator_traits::template rebind_alloc; using MemoryAllocatorTraits = typename std::allocator_traits; MemoryAllocator memory_allocator = alloc; auto ptr = MemoryAllocatorTraits::allocate(memory_allocator, sizeof(T) + 2 * sizeof(size_t) + sizeof(Deleter)); using ConstructAllocator = typename std::allocator_traits::template rebind_alloc; using ConstructAllocatorTraits = typename std::allocator_traits; ConstructAllocator construct_allocator = alloc; ConstructAllocatorTraits::construct(construct_allocator, (T*) ptr, std::forward(args)...); *(size_t*) (ptr + sizeof(T)) = 0; *(size_t*) (ptr + sizeof(T) + sizeof(size_t)) = 0; return SharedPtr((T*) ptr, (size_t*) (ptr + sizeof(T)), (size_t*) (ptr + sizeof(T) + sizeof(size_t)), (Deleter*) (ptr + sizeof(T) + 2 * sizeof(size_t)), alloc); } template SharedPtr makeShared(Args&& ... args) { return allocateShared(std::allocator(), std::forward(args)...); }