C++标准库中又两种智能指针分别为:shared_ptr和unique_ptr
shared_ptr和unique_ptr之间最大的区别就是:shared_ptr允许共享指针,而unique_ptr则独占指针。
还有一个差异就是两种指针的删除器的差异。对于shared_ptr来说,删除器是可以重载的,所以其类型是在运行时绑定。而unique_ptr的删除器不能重载,且是unique_ptr类的一部分,在其编译时绑定。
根据以上这两点,我们对于实现也要采取不同的策略。
首先是shared_ptr的实现。
根据之前提到的,shared_ptr允许共享指针,那么我们就要做一个指针的引用计数,思路是使用new表达式来统计引用计数,每有一个指针被赋值则引用计数加一,相反,当指针被delete时引用计数减一。
先贴代码,如下:
#include <iostream>
#include <functional>
using namespace std;
class deleter{
public:
deleter()= default;
template<typename T>
void operator()(T* p)const{
delete p;
p=nullptr;
cout<<"new deleter"<<endl;
}
};
template<typename T>
class New_shared_ptr{
public:
New_shared_ptr()=default;
explicit New_shared_ptr(T *p,function<void(T*)> d=deleter()):ptr(p),use(new size_t(1)),del(d){} //explicit
New_shared_ptr(const New_shared_ptr&p):ptr(p.ptr),use(p.use),del(p.del){++*use;} //copy
New_shared_ptr(New_shared_ptr&&p) noexcept :ptr(p.ptr),use(p.use),del(std::move(p.del)){ //move
p.ptr=nullptr;
p.use=nullptr;
}
T &operator*();
T* operator->();
New_shared_ptr &operator=(const New_shared_ptr&);
New_shared_ptr &operator=(New_shared_ptr &&) noexcept ;
operator bool() const { return ptr ? true : false;}
T* get(New_shared_ptr&);
bool unique();
size_t use_count();
void reset(){decrement_n_destroy();} //一共3个重载版本
void reset(T *p){
if(ptr!=p){
decrement_n_destroy();
ptr=p;
use=new size_t(1);
}
}
void reset(T *p,const function<void(T*)> &d){
reset(p);
del=d;
}
~New_shared_ptr();
private:
T* ptr= nullptr;
size_t *use=new size_t(0); //引用计数
function<void(T*p)> del{deleter()};
void decrement_n_destroy();
};
template <typename T>
T* New_shared_ptr<T>::operator->() {
return & this->operator *();
}
template <typename T>
T& New_shared_ptr<T>::operator*() {
return *ptr;
}
template <typename T>
New_shared_ptr<T>& New_shared_ptr<T>::operator=(const New_shared_ptr &rhs) {
++*rhs.use;
if(--*use==0){
delete use;
}
use=rhs.use;
del=rhs.del;
return *this;
}
template <typename T>
New_shared_ptr<T>&
New_shared_ptr<T>::operator=(New_shared_ptr &&p) noexcept{
decrement_n_destroy();
std::swap(this->use,p.use);
std::swap(this->ptr,p.ptr);
std::swap(this->del,p.del);
}
template<typename T>
T* New_shared_ptr<T>::get(New_shared_ptr&){
return ptr;
}
template <typename T>
size_t New_shared_ptr<T>::use_count(){
return *use;
}
template <typename T>
bool New_shared_ptr<T>::unique() {
return *use==1;
}
template <typename T>
New_shared_ptr<T>::~New_shared_ptr() {
decrement_n_destroy();
}
template <typename T>
inline void
New_shared_ptr<T>::decrement_n_destroy()
{
if(ptr)
{
if (--*use == 0)
{
delete use;
del(ptr);
}
use = nullptr;
ptr = nullptr;
}
}
deleter是我们定义的删除器,也就是封装了指针的类,在这里用到了function模版类型。用来保存可调用对象,也就是保存我们自己定义可以用来重载的删除器,其形式可以是函数指针,函数,lambda表达式,bind函数以及重载了函数调用运算符的类。
其他部分的实现比较简单,主要注意的就是:要注意赋值和移动运算符中可能会出现自赋值的情况。
下面是unique_ptr的实现
首先说想法,unique_ptr是独占指针的,所以在实现的时候是不需要引用计数的,其次unique_ptr没有赋值操作(但是有移动操作!)
其次,unique_ptr的删除器是类的成员(可以自己指定)
代码如下:
#include <string>
#include <iostream>
#include "deleter.h"
using namespace std;
template<typename,typename>
class New_unique_ptr;
template<typename T,typename D>
void swap(New_unique_ptr<T,D> &p1,New_unique_ptr<T,D> &p2){
std::swap(p1.ptr,p2.ptr);
std::swap(p1.del,p2.del);
}
template<typename T,class D=deleter>
class New_unique_ptr{
friend void swap<T,D>(New_unique_ptr<T,D> &p1,New_unique_ptr<T,D> &p2);
public:
New_unique_ptr()=default;
explicit New_unique_ptr(T*p,D d1=deleter()):ptr(p),del(d1){}
//unique_ptr没有赋值操作
New_unique_ptr(const New_unique_ptr& up)=delete;
New_unique_ptr&operator=(const New_unique_ptr&)=delete;
//移动操作要将指向的内容一起移动
New_unique_ptr(New_unique_ptr&& up)noexcept :ptr(up.ptr),del(up.del){up.ptr= nullptr;}
New_unique_ptr& operator =(New_unique_ptr&& rhs) noexcept;
New_unique_ptr&operator=(nullptr_t p)noexcept;
//重载操作
T& operator*(){ return *ptr; }
T* operator->(){ return &this->operator*();}
~New_unique_ptr();
T* get() const noexcept { return ptr;}
T* release();
void reset();
void reset(T* p);
private:
T* ptr= nullptr;
D del=deleter();
};
template<typename T,class D>
New_unique_ptr<T,D>& New_unique_ptr<T,D>::operator=(New_unique_ptr&& rhs) noexcept{
if(this->ptr!=rhs.ptr){
del(ptr);
ptr= nullptr;
::swap(*this,rhs);
}
return *this;
};
template<typename T,class D>
New_unique_ptr<T,D>& New_unique_ptr<T,D>::operator=(nullptr_t p)noexcept{
if(p==nullptr){
del(ptr);
ptr=nullptr;
}
return *this;
};
template<typename T,class D>
T * New_unique_ptr<T,D>::release(){
T *p=ptr;
ptr=nullptr;
return p;
};
template<typename T,class D>
void New_unique_ptr<T,D>::reset(){
del(ptr);
ptr=nullptr;
};
template<typename T,class D>
void New_unique_ptr<T,D>::reset(T* p){
del(ptr);
ptr=p;
};
template<typename T,class D>
New_unique_ptr<T,D>::~New_unique_ptr(){
del(ptr);
};
删除器的实现如下:
#include <iostream>
using namespace std;
class deleter{
public:
deleter(){}
template<typename T>
void operator()(T* p){
delete p;
p=nullptr;
cout<<"already delete"<<endl;
}
};
说下自己的收获吧,造轮子对于刚学习完入门C++的人来说很重要,打好基础才是最重要的。实现完智能指针,自己对于模版,类,重载又有类新的认识。
还有一个我觉得比较容易忽视的一个点就是
对于operator->()的理解,成员访问运算符返回值是指针类型。对于它的原理大家可以再重新看一遍,可能会有不一样的收获哦。