Edit: NOTE I'm a C++ "beginner" still in undergrad trying to teach myself modern C++ (because they don't do that in uni) so I'm sure this is riddled with errors that I am unaware of.
Made a subset of std's smart pointer library (unique, shared, and weak, denoted as unq, shr, and weak) in a very minimal header file. This is mostly for fun and as a learning experience but looking to improve in any way, thanks!
ptr.h
#ifndef PTRLIB_H
#define PTRLIB_H
#include <cstdint>
#include <atomic>
#include <iostream>
namespace ptr
{
template<typename T>
class base //for methods common to all smart ptr types
{
protected:
mutable T * obj;
//non instatiable outside the header
base() {}
base(T * obj) : obj(obj) {}
virtual void operator = (T * obj) { this->obj = obj; }
public:
//unq uses these versions
virtual void reset() { delete this->obj; this->obj = nullptr; }
virtual void reset(T * obj) { delete this->obj; this->obj = obj; }
inline T * get() { return obj; }
operator bool const () { return (obj != nullptr) ? true : false; }
bool operator == (const base<T> rhs) { return obj == rhs.obj; }
bool operator != (const base<T> rhs) { return obj != rhs.obj; }
bool operator <= (const base<T> rhs) { return obj <= rhs.obj; }
bool operator >= (const base<T> rhs) { return obj >= rhs.obj; }
bool operator < (const base<T> rhs) { return obj < rhs.obj; }
bool operator > (const base<T> rhs) { return obj > rhs.obj; }
std::ostream & operator << (std::ostream & stream) { return (std::cout << obj); }
};
template<typename T>
class unq : public base<T>
{
public:
unq() {}
unq(T * obj) : base<T>(obj) {}
unq(const unq<T> & u) : base<T>(u.obj) { u.obj = nullptr; }
~unq() { delete this->obj; }
T * release()
{
T * temp = this->obj;
this->obj = nullptr;
return temp;
}
//don't want weak to be able to access the object so duplicated in shr
inline T * operator -> () { return this->obj; }
inline T & operator * () { return *(this->obj); }
};
template<typename T>
class weak; //class forwarding for friend class
template<typename T>
class shr : public base<T>
{
private:
friend class weak<T>;
//reference counter
mutable std::atomic<int32_t> * refs;
inline bool is_last() { return ((refs == nullptr && this->obj == nullptr) || *refs == 1); }
public:
shr()
{ refs = new std::atomic<int32_t>, *refs = 1; }
shr(T * obj) : base<T>(obj)
{ refs = new std::atomic<int32_t>, *refs = 1; }
shr(const shr<T> & s) : base<T>(s.obj)
{ refs = (s.refs != nullptr) ? s.refs : new std::atomic<int32_t>, *refs += 1; }
shr(const weak<T> & w) : base<T>(w.obj)
{ refs = (w.refs != nullptr) ? w.refs : new std::atomic<int32_t>, *refs += 1; }
~shr()
{
if (is_last())
{
delete this->obj; this->obj = nullptr;
delete refs; refs = nullptr;
}
else *refs -= 1;
}
void operator = (T * obj)
{
this->obj = obj;
*refs = 1;
}
void operator = (const shr<T> & s)
{
this->obj = s.obj;
refs = (s.refs != nullptr) ? s.refs : new std::atomic<int32_t>, *refs += 1;
}
void operator = (const weak<T> & w)
{
this->obj = w.obj;
refs = (w.refs != nullptr) ? w.refs : new std::atomic<int32_t>, *refs += 1;
}
void reset()
{
if (is_last())
{
delete this->obj; this->obj = nullptr;
delete refs; refs = nullptr;
}
else
{
this->obj = nullptr;
*refs -= 1; refs = nullptr;
}
}
void reset(T * obj)
{
if (is_last()) { delete this->obj; delete refs; }
else *refs -= 1;
this->obj = obj;
refs = new std::atomic<int32_t>, *refs = 1;
}
inline const int32_t use_count() { return static_cast<int32_t>(*refs); }
inline bool unique() { return (*refs == 1); }
inline T * operator -> () { return this->obj; }
inline T & operator * () { return *(this->obj); }
};
template<typename T>
class weak : public base<T>
{
private:
friend class shr<T>;
mutable std::atomic<int32_t> * refs;
public:
weak() {}
weak(T * obj) : base<T>(obj) {}
weak(const weak<T> & w) : base<T>(w->obj) {}
weak(const shr<T> & s) : base<T>(s.obj), refs(s.refs) {}
void operator = (T * obj) { this->obj = obj; }
void operator = (const shr<T> & s) { this->obj = s.obj; refs = s.refs; } void operator = (const weak<T> & w) { this->obj = w.obj; refs = w.refs; }
void reset() { this->obj = nullptr; refs = nullptr; }
void reset(T * obj) { this->obj = obj; refs = new std::atomic<int32_t>; *refs = 0; }
inline shr<T> lock() { return shr<T>(this->obj); }
inline bool expired() { return ((refs == nullptr || *refs <= 0) ? true : false); }
inline const int32_t use_count() { return ((expired()) ? 0 : static_cast<int32_t>(*refs)); }
};
template<typename T>
const shr<T> make_shr(T * obj) { return shr<T>(obj); }
template<typename T>
const unq<T> make_unq(T * obj) { return unq<T>(obj); }
}
#endif