实现一个简单的 shared_ptr

智能指针的作用有如同指针,但会记录有多少个 shared_ptrs 共同指向一个对象。这便是所谓的引用计数。一旦最后一个这样的指针被销毁,也就是一旦某个对象的引用计数变为 0,这个对象会被自动删除。

shared_ptr 的实现机制其实就是在拷贝构造时使用同一份引用计数。

同一个 shared_ptr 被多个线程 “读” 是安全的,同一个 shared_ptr 被多个线程 “写” 是不安全的。

分析

比如说,用智能指针来创建一个动态分配的字符串对象:

// 新创建一个对象,引用计数器为 1
shared_ptr<string> pstr(new string("abc"));

解引用一个智能指针返回它指向的对象。同样,我们可以像操作普通指针一样调用 string 提供的方法。

if (pstr && pstr->empty()) {
        *pstr = "hello";
}

当有另外一个智能指针对当前智能指针进行拷贝时,引用计数器加 1:

shared_ptr<string> pstr(new string("abc")); // pstr 指向的对象只有一个引用者
shared_ptr<string> pstr2(pstr); // pstr 跟 pstr2 指向相同的对象,此对象有两个引用者

当两个智能指针进行赋值操作时,左边的指针指向的对象引用计数减 1,右边的加 1。

shared_ptr<string> pstr(new string("abc"));
shared_ptr<string> pstr2(new string("hello"));

// 给 pstr2 赋值,令他指向另一个地址,递增 pstr 指向的对象的引用计数,递减 pstr2 原来指向的对象引用计数
pstr2 = pstr; 

指针离开作用域范围时,同样引用计数减1。当引用计数为0时,对象被回收。

简单实现

template <typename T>
class smart_ptrs 
{
  public:
    smart_ptrs(T*); // 用普通指针初始化智能指针
    smart_ptrs(smart_ptrs&);

    T* operator->(); // 自定义指针运算符
    T& operator*(); // 自定义解引用运算符
    smart_ptrs& operator=(smart_ptrs&); // 自定义赋值运算符

    ~smart_ptrs(); // 自定义析构函数

  private:
    int *count; // 引用计数
    T *p; // 智能指针底层保管的指针
};

跟标准库一样,我们使用模板来实现它。
用普通指针进行初始化时,需要将该指针进行封装,并且引用计数初始化为1。

template <typename T>
smart_ptrs<T>::smart_ptrs(T *p): count(new int(1)), p(p) {
}

定义拷贝构造函数:

template <typename T>
// 对普通指针进行拷贝,同时引用计数器加 1,因为需要对参数进行修改,所以没有将参数声明为 const
smart_ptrs<T>::smart_ptrs(smart_ptrs &sp): count(&(++*sp.count)), p(sp.p) {
}

定义指针运算符:

template <typename T>
T* smart_ptrs<T>::operator->() {
    return p;
}

定义解引用运算符,直接返回底层指针的引用:

template <typename T>
T& smart_ptrs<T>::operator*() {
    return *p;
}

定义赋值运算符,左边的指针计数减 1,右边指针计数加 1,当左边指针计数为 0 时,释放内存:

template <typename T>
smart_ptrs<T>& smart_ptrs<T>::operator=(smart_ptrs& sp) {
    ++*sp.count;
    if (--*count == 0) { // 自我赋值同样能保持正确
        delete count;
        delete p;
    }
    this->p = sp.p;
    this->count = sp.count;
    return *this;
}

定义析构函数:

template <typename T>
smart_ptrs<T>::~smart_ptrs() {
    if (--*count == 0) {
        delete count;
        delete p;
    }
}

大功告成!

后面是个小测试:

#include <iostream>
using namespace std;

template <typename T>
class smart_ptrs 
{
  public:
    smart_ptrs(T*); // 用普通指针初始化智能指针
    smart_ptrs(smart_ptrs&);

    T* operator->(); // 自定义指针运算符
    T& operator*(); // 自定义解引用运算符
    smart_ptrs& operator=(smart_ptrs&); // 自定义赋值运算符

    ~smart_ptrs(); // 自定义析构函数

  private:
    int *count; // 引用计数
    T *p; // 智能指针底层保管的指针
};

// 定义构造函数
template <typename T>
smart_ptrs<T>::smart_ptrs(T *p): count(new int(1)), p(p) {
}

// 定义拷贝构造函数
template <typename T>
// 对普通指针进行拷贝,同时引用计数器加 1,因为需要对参数进行修改,所以没有将参数声明为 const
smart_ptrs<T>::smart_ptrs(smart_ptrs &sp): count(&(++*sp.count)), p(sp.p) {
}

// 定义指针运算符
template <typename T>
T* smart_ptrs<T>::operator->() {
    return p;
}

// 定义解引用运算符,直接返回底层指针的引用
template <typename T>
T& smart_ptrs<T>::operator*() {
    return *p;
}

// 定义赋值运算符,左边的指针计数减 1,右边指针计数加 1,当左边指针计数为 0 时,释放内存
template <typename T>
smart_ptrs<T>& smart_ptrs<T>::operator=(smart_ptrs& sp) {
    ++*sp.count;
    if (--*count == 0) { // 自我赋值同样能保持正确
        delete count;
        delete p;
    }
    this->p = sp.p;
    this->count = sp.count;
    return *this;
}

// 定义析构函数
template <typename T>
smart_ptrs<T>::~smart_ptrs() {
    if (--*count == 0) {
        delete count;
        delete p;
    }
}

// test
int main()
{
  smart_ptrs<string> pstr(new string("abc"));
  smart_ptrs<string> pstr2(pstr);
  smart_ptrs<string> pstr3(new string("bcd"));
  pstr3 = pstr2;
  return 0;
}

直接用 gdb 看每一步的输出结果就 OK 了:

p *pstr.count
p *pstr.p

四句话的输出分别是:

1 
"abc"

2 
"abc"

2 
"abc"

3 
"abc"
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值