最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

当前速读:智能指针基本原理,简单实现,常见问题

来源:博客园


(资料图片)

基本概念

  • 智能指针是一个模板;
  • shared_ptr允许多个指针指向同一个对象,unique指针则独占指向的对象;

基本使用

  • shared_ptr ptr; //默认初始化保存着一个空指针

  • shared_ptr ptr = make_shared(42);

  • 拷贝与赋值,会有一个引用计数

    引用计数增加的情况:

    • 拷贝初始化:shared_ptrq(p);
    • 参数传递及函数返回值:void function(shared_ptr ptr);因为这也是一种拷贝

基本实现

  • 两个基本成员ptr与ref_count,即指针与引用计数,关于引用计数是一个数值还是一个稍复杂的类,要看库的具体实现,此处为了简便使用一个数值来统计引用计数;

    ptr
    ref_count
  • 需实现函数:

    • 显式初始化构造函数
    • 拷贝构造函数
    • 析构函数
#include#include#includeusing namespace std;templateclass Shared_Ptr{public:Shared_Ptr(T* ptr = nullptr) // 默认构造函数:_pPtr(ptr), _pRefCount(new int(1)), _pMutex(new mutex){}~Shared_Ptr()  // 定制析构函数{Release();}Shared_Ptr(const Shared_Ptr& sp)  // 拷贝构造函数,增加引用计数:_pPtr(sp._pPtr), _pRefCount(sp._pRefCount), _pMutex(sp._pMutex){AddRefCount();}Shared_Ptr& operator=(const Shared_Ptr& sp) // 重载赋值运算符{//if (this != &sp)if (_pPtr != sp._pPtr){// 释放管理的旧资源Release();// 共享管理新对象的资源,并增加引用计数_pPtr = sp._pPtr;_pRefCount = sp._pRefCount;_pMutex = sp._pMutex;AddRefCount();}return *this;}T& operator*(){return *_pPtr;}T* operator->(){return _pPtr;}int UseCount() { return *_pRefCount; }T* Get() { return _pPtr; }void AddRefCount(){_pMutex->lock();++(*_pRefCount);_pMutex->unlock();}private:void Release(){bool deleteflag = false;_pMutex->lock();if (--(*_pRefCount) == 0){delete _pRefCount;delete _pPtr;deleteflag = true;}_pMutex->unlock();if (deleteflag == true)delete _pMutex;}private:int *_pRefCount; // 计数器T* _pPtr;        // 指针成员mutex* _pMutex;  };

线程安全问题

  • 引用计数是多个智能指针对象共享,若智能指针处于不同的线程内,则线程并行操作有可能引起技术混乱或指针空悬问题;
  • 计数混乱:为了解决计数混乱问题,可以加互斥锁在计数变量上,这样每次只会有一个线程执行变量的加减操作;即在实现中引用计数的操作是线程安全的
  • 指针空悬:对指针的操作不是线程安全的。如对于多线程中的两个智能指针a,b,若在某线程1中想要执行赋值操作a=b,分两步执行,1)先执行指针的复制,2)再执行引用计数的复制并加1,这两步操作不是原子的. 如果执行完第一步后,转到了线程2执行b = (new ClassA),即对于智能指针b来说,他被赋予了新值,原计数减一,由于线程一中的增加引用计数操作还未来得及实施,所以现在引用计数变成了0,原指针被释放。如果此时再回到线程1,a的引用计数指针将指向新的b的引用计数,这里就产生了错误,并且a的指针空悬,产生安全问题。
  • 具体图示可见link:https://blog.csdn.net/liang19890820/article/details/120465794

循环引用

可以假设一个双向链表Node的结构体,其中的next与prev都设置为智能指针,然后创建两个智能指针node1, ndoe2指向两个新new的Node,那么这时候两者的引用计数都是1.此时再分别设置node1->next = node2; node2->next = node1,两者的引用计数都变为2,若此时程序结束,则两node引用值都变为1,无法析构,。想要node1析构得先析构node2,反之亦然,这就导致了循环。

  • 解决方法:shared_ptr改为weak_ptr,弱指针不会增加引用计数,就不会循环引用了

关键词: