I understand that shared_ptr offers some thread safety. The guarantee seems to be that if multiple shared_ptr instances are destroyed at the same time, the object is destroyed only once. The case seems to be that nothing else about the smart pointer is thread safe, i.e., copying, moving, etc. And that makes sense as that would be difficult to achieve without explicit use of mutexes. Having said that, would an implementation like this be thread-safe in the way the standard decrees?
#include <atomic>
#include <utility>
template<class T> struct SharedPtr {
private:
std::atomic<int> *pRefCnt_ = nullptr;
T *pObj_ = nullptr;
void destroy() {
if (pRefCnt_ && *pRefCnt_-- == 0) {
delete std::exchange(pRefCnt_, nullptr);
delete std::exchange(pObj_, nullptr);
}
}
void copy(SharedPtr const &other) {
pRefCnt_ = other.pRefCnt_;
pObj_ = other.pObj_;
if (pRefCnt_) {
++*pRefCnt_;
}
}
void move(SharedPtr &&other) {
pRefCnt_ = std::exchange(other.pRefCnt_, nullptr);
pObj_ = std::exchange(other.pObj_, nullptr);
}
public:
SharedPtr() = default;
explicit SharedPtr(T *p) : pRefCnt_(new std::atomic<int>(0)), pObj_(p) {}
SharedPtr(SharedPtr const &rhs) noexcept { copy(rhs); }
SharedPtr(SharedPtr &&rhs) noexcept { move(std::move(rhs)); }
SharedPtr &operator=(SharedPtr const &rhs) noexcept {
if (this != &rhs) {
destroy();
copy(rhs);
}
return *this;
}
SharedPtr &operator=(SharedPtr &&rhs) noexcept {
if (this != &rhs) {
destroy();
move(std::move(rhs));
}
return *this;
}
~SharedPtr() { destroy(); }
T *operator*() const { return pObj_; }
T &operator->() const { return *pObj_; }
explicit operator bool() const { return pObj_ != nullptr; }
};