从零开始写STL - 智能指针
- 智能指针的分类及其特点:
scoped_ptr:初始化获得资源控制权,在作用域结束释放资源
shared_ptr: 引用计数来控制共享资源,最后一个资源的引用被释放的时候会析构
unique_ptr: 只能有一个持有资源引用的对象
weak_ptr:eak_ptr也维护了一个引用计数,跟shared_ptr维护的引用计数或互不干扰,或相互协同。weak_ptr的指针会在weak_ptr维护的引用计数上加一,而shared_ptr会在shared_ptr维护的引用计数上加一,这样在循环引用时,就会因为对不同引用的判断的不同,使最终决定是否释放空间的结果也不相同。
shared_ptr
实现的几个问题:
引用计数如何保存?
每个shared_ptr 维护一个指针,指向共享引用计数
在哪里进行对象析构?
在引用计数为0时
std::shared_ptr is a smart pointer that retains shared ownership of an object through a pointer. Several shared_ptr objects may own the same object. The object is destroyed and its memory deallocated when either of the following happens
the last remaining shared_ptr owning the object is destroyed;
the last remaining shared_ptr owning the object is assigned another pointer via operator= or reset().The object is destroyed using delete-expression or a custom deleter that is supplied to shared_ptr during construction.
A shared_ptr can share ownership of an object while storing a pointer to another object. This feature can be used to point to member objects while owning the object they belong to. The stored pointer is the one accessed by get(), the dereference and the comparison operators. The managed pointer is the one passed to the deleter when use count reaches zero.A shared_ptr may also own no objects, in which case it is called empty (an empty shared_ptr may have a non-null stored pointer if the aliasing constructor was used to create it).All specializations of shared_ptr meet the requirements of CopyConstructible, CopyAssignable, and LessThanComparable and are contextually convertible to bool.All member functions (including copy constructor and copy assignment) can be called by multiple threads on different instances of shared_ptr without additional synchronization even if these instances are copies and share ownership of the same object. If multiple threads of execution access the same shared_ptr without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur; the shared_ptr overloads of atomic functions can be used to prevent the data race.
template<class T>
class shared_ptr_node
{
public:
shared_ptr_node() :cnt(0) {}
shared_ptr_node(T* _ptr)
{
ptr = _ptr, cnt = 1;
}
~shared_ptr_node()
{
}
void increment()
{
cnt++;
}
void decrement()
{
if (cnt != 0)
{
cnt--;
if (cnt == 0)
{
//析构
delete ptr;
//避免野指针
ptr = nullptr;
}
}
}
T* get_ptr()
{
return ptr;
}
size_t count()
{
return cnt;
}
private:
//引用计数
size_t cnt;
//共享元素
T* ptr;
};
template<class T>
class shared_ptr
{
typedef shared_ptr_node<T> node;
typedef shared_ptr_node<T>* node_ptr;
typedef shared_ptr<T> self;
private:
node_ptr ptr = nullptr;
public:
shared_ptr()noexcept
{
ptr = new node();
}
shared_ptr(const self& x)
{
if (this->ptr != x.ptr)
{
if (ptr != nullptr)
ptr->decrement();
if (x.ptr != nullptr)
x.ptr->increment();
ptr = x.ptr;
}
}
explicit shared_ptr(T* data_ptr)
{
ptr = new node(data_ptr);
}
//注意维护引用计数
self& operator= (const self& x) noexcept
{
if (this->ptr == x.ptr)
return *this;
if (ptr != nullptr)
ptr->decrement();
if (x.ptr != nullptr)
x.ptr->increment();
ptr = x.ptr;
return *this;
}
T& operator*()
{
return *(ptr->get_ptr());
}
size_t use_count()
{
return ptr->count();
}
bool unique() const noexcept
{
return use_count() == 1;
}
void swap(shared_ptr& x) noexcept
{
std::swap(x.ptr, ptr);
}
void reset() noexcept
{
ptr->decrement();
ptr = new node();
}
template <class U> void reset(U* p)
{
ptr->decrement();
ptr = new shared_ptr_node<U>(p);
}
T* get() noexcept
{
return ptr->ptr;
}
~shared_ptr()
{
if(ptr != nullptr)
{
ptr->decrement();
ptr = nullptr;
}
}
};
template<class T, class...Args>
shared_ptr<T> make_shared(Args... args)
{
return shared_ptr<T>(new T(std::forward<Args>(args)...));
}
unique_ptr
template<class T>
class unique_ptr
{
public:
unique_ptr() :data(nullptr) {}
unique_ptr(T* _data) :data(_data) {}
T* release()
{
T* ret = data;
data = nullptr;
return ret;
}
void reset()
{
delete data;
data = nullptr;
}
void reset(T* _data)
{
delete data;
data = _data;
}
void operator =(const unique_ptr<T>& rhs)
{
if (rhs.data != nullptr)
reset(rhs.release());
else
reset();
}
T& operator*()
{
return *data;
}
private:
T* data;
};
weak_ptr (todo)
- std::weak_ptr is a smart pointer that holds a non-owning ("weak") reference to an object that is managed by std::shared_ptr. It must be converted to std::shared_ptr in order to access the referenced object.std::weak_ptr models temporary ownership: when an object needs to be accessed only if it exists, and it may be deleted at any time by someone else, std::weak_ptr is used to track the object, and it is converted to std::shared_ptr to assume temporary ownership. If the original std::shared_ptr is destroyed at this time, the object‘s lifetime is extended until the temporary std::shared_ptr is destroyed as well.In addition, std::weak_ptr is used to break circular references of std::shared_ptr.