C++——智能指针 shared_ptr
C11中开始提供更靠谱的并且支持拷贝的shared_ptrRAII 具有指针类似的行为 引用计数目录一、shared_ptr介绍二、shared_ptr的使用编辑三、shared_ptr的模拟实现版本一使用一个int类型内置成员变量再进行拷贝构造的时候进行一次 之后赋值给新对象。版本二考虑到使用static静态成员变量可以为对象之间所共享版本三不使用静态成员变量借助指针类型变量版本四为了线程安全引入锁机制在所有计数操作时加锁保证操作原子性加入DFDef保证析构的重载四、涉及自定义类型智能指针变量线程安全实例一、shared_ptr介绍shared_ptr的原理是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。1. shared_ptr在其内部给每个资源都维护了着一份计数用来记录该份资源被几个对象共享。2.在对象被销毁时(也就是析构函数调用)就说明自己不使用该资源了对象的引用计数减一。3.如果引用计数是0就说明自己是最后一个使用该资源的对象必须释放该资源4.如果不是0就说明除了自己还有其他对象在使用该份资源不能释放该资源否则其他对象就成野指针了。二、shared_ptr的使用#includeiostream using namespace std; void TestSharedPtr() { shared_ptrint up1(new int(10)); shared_ptrint up2(up1); shared_ptrint up3; up3 up1; } int main() { TestSharedPtr(); _CrtDumpMemoryLeaks(); return 0; }三、shared_ptr的模拟实现资源可以共享浅拷贝的基础上可以保证资源无论被多少对象共享最终只会释放一次。版本一使用一个int类型内置成员变量再进行拷贝构造的时候进行一次 之后赋值给新对象。问题 计数有问题不同对象之间无法实现同步共享一个计数变量下图就是在进行up2析构之后发现up1和up2的计数变量_count是不同值。namespace wei { templateclass T class shared_ptr { shared_ptr(T* ptr nullptr) : _ptr(ptr) , _count(0) { if (_ptr) { _count 1; } } ~shared_ptr() { if (_ptr 0 --_count) { delete _ptr; _ptr nullptr; } } shared_ptr(shared_ptrT sp) : _ptr(sp._ptr) , _count(sp._count) {} private: T* _ptr; int _count; }; }版本二考虑到使用static静态成员变量可以为对象之间所共享析构 up2之前析构up2之后问题当另外新的对象来的时候就会产生问题这里的up3将所有对象所共享的_count重新经历了一次初始化也因为所有对象共享一个_count导致了问题。我们希望每一类对象都有属于他们的_countup1和拷贝构造出来的up2可以使用一个_count而新声明出来的up3对象又去使用他的_count我们不希望他们之间进行冲突。namespace wei { templateclass T class shared_ptr { public: shared_ptr(T* ptr nullptr) : _ptr(ptr) { if (_ptr) { _count 1; } } ~shared_ptr() { if (_ptr 0 --_count) { delete _ptr; _ptr nullptr; } } shared_ptr(shared_ptrT sp) : _ptr(sp._ptr) { _count; } private: T* _ptr; static int _count; // 静态成员变量 }; templateclass T int shared_ptrT::_count 0;// 类内声明类外定义 }版本三不使用静态成员变量借助指针类型变量参考我们不同对象在创建时都有不同的地址空间例如up1和up2中的_ptr就使用同一份地址而up3中的_ptr又有它自己新的地址能不能利用类似于_ptr的一个东西来设计一个_count存储每一堆对象们的计数。在成员变量中新加一个指针来进行计数。private: T* _ptr; int* _pcount;效果如下图namespace wei { templateclass T class shared_ptr { public: shared_ptr(T* ptr nullptr) : _ptr(ptr) , _pcount(nullptr) { if (_ptr) { _pcount new int(1); } } ~shared_ptr() { if (_ptr 0 --(*_pcount)) { delete _ptr; delete _pcount; _ptr nullptr; _pcount nullptr; } } shared_ptr(shared_ptrT sp) : _ptr(sp._ptr) , _pcount(sp._pcount) { (*_pcount); } private: T* _ptr; int* _pcount; };版本四为了线程安全引入锁机制在所有计数操作时加锁保证操作原子性加入DFDef保证析构的重载#include mutex namespace bite { templateclass T class DFDef { public: void operator()(T* ptr) { if (ptr) { delete ptr; ptr nullptr; } } }; templateclass T, class DF DFDefT class shared_ptr { public: // RAII shared_ptr(T* ptr nullptr) : _ptr(ptr) , _pcount(nullptr) , _pMutex(nullptr) { if (_ptr) { _pcount new int(1); _pMutex new mutex(); } } ~shared_ptr() { Release(); } // 具有指针类似行为 T operator*() { return *_ptr; } T* operator-() { return _ptr; } // 解决浅拷贝引用计数 shared_ptr(const shared_ptrT sp) : _ptr(sp._ptr) , _pcount(sp._pcount) , _pMutex(sp._pMutex) { AddRef(); } shared_ptrT operator(const shared_ptrT sp) { if (this ! sp) { // *this要和sp去共享资源 // 1. 先让*this和离开之前的资源 Release(); // 2. *this 和 sp共享资源和计数 _ptr sp._ptr; _pcount sp._pcount; _pMutex sp._pMutex; AddRef(); } return *this; } T* get() { return _ptr; } private: void AddRef() { _pMutex-lock(); (*_pcount); _pMutex-unlock(); } void Release() { bool flag false; _pMutex-lock(); if (_ptr 0 --(*_pcount)) { DF()(_ptr); delete _pcount; _pcount nullptr; flag true; } _pMutex-unlock(); if (flag) { delete _pMutex; _pMutex nullptr; } } private: T* _ptr; int* _pcount; mutex* _pMutex; }; }四、涉及自定义类型智能指针变量线程安全实例#include thread #include mutex struct Date { int _year 0; int _month 0; int _day 0; }; void SharePtrFunc(std::shared_ptrDate sp, size_t n, mutex mtx) { cout sp.get() endl; for (size_t i 0; i n; i) { std::shared_ptrDate copy(sp); // unique_lockmutex lk(mtx); 保证该区域元素修改原子性 copy-_year; copy-_month; copy-_day; } } void TestSharedPtr3() { std::shared_ptrDate sp(new Date); cout sp.get() endl; size_t n 10000; mutex m; thread t1(SharePtrFunc, std::ref(sp), n, std::ref(m)); ref的用法保证传递引用 thread t2(SharePtrFunc, std::ref(sp), n, std::ref(m)); t1.join(); 进行线程等待 t2.join(); cout sp-_year endl; cout sp-_month endl; cout sp-_day endl; } int main() { TestSharedPtr3(); _CrtDumpMemoryLeaks(); return 0; }