文章目录
- 4.智能指针[shared_ptr]
- 4.1设计理念
- 成员属性
- 4.2主要接口
- 拷贝构造
- 4.3引用计数线程安全问题
- 测试线程安全
- 通过对计数引用的加锁保护使得类线程安全
- 类实例化的对象使用时需要手动加锁保护
- "锁"的引进
- 线程引用传参问题
- 4.4整体代码
- 5.循环引用问题
- 5.1问题的引入
- 5.2分析造成此问题的原因
- 5.3weak_ptr的主要代码
- 6.数组对象的删除问题
- 6.1代码问题
- 6.2std::shared_ptr面对此问题的解决方案
- 1.首先看std::shared_ptr::~shared_ptr
- 2.删除器的传参及使用
- 3.添加封装删除器
- 7.总结
- 7.1完整代码
- 7.2C++11和Boost智能指针的关系
4.智能指针[shared_ptr]
4.1设计理念
成员属性
每一个对象除了有一个主指针外 还有一个副指针用来计数 为什么不设置成int而设置成int*?
同一块空间被两个指针指向 当编译器得到需要销毁指针的指令时 会先判断这是不是最后一个指针 即count==1 时才释放空间 其余情况均只是的计数-- 而不释放空间 这是我们的最初目的 如果设置成int意味着每一个对象有各自的count 当ptr1拷贝给ptr2 我们想要的是 有一块空间单独来计数 如果执行拷贝则计数++ 即我们需要一块共有的空间来实现
不是要实现共有吗 为什么不直接用静态变量?
静态变量可以实现共有没错 但他是当前类的所有对象共有 举例说明: 代码的本意是ptr1 ptr2 ptr3指向同一块空间 此时count == 3 ptr4指向另一块空间时 代码的本意是count = 1 但是同时改变了ptr1/2/3
4.2主要接口
拷贝构造
//赋值重载//1."自己"给"自己"赋值//不仅仅是p1 = p1 还要考虑p2 = p1 但之前p2就 = p1//2.左 = 右 赋值后 // 左指针指向空间的指针少了一个 左指针的count-- 进一步考虑count--后==0的情况// 右指针指向空间的指针多了一个 右指针的count++shared_ptr<T>& operator=(const shared_ptr<T>& sp){//"自己"给"自己"赋值: "自己"的本质if (_ptr != sp._ptr){//count--Release();_ptr = sp._ptr;_pcount = sp._pcount;_pmtx = sp._pmtx;//count++AddCount();}return *this;}
4.3引用计数线程安全问题
测试线程安全
通过对计数引用的加锁保护使得类线程安全
struct Date{int _year = 0;int _month = 0;int _day = 0;};void SharePtrFunc(ape::shared_ptr<Date>& sp, size_t n, mutex& mtx){cout << " SharePtrFunc: sp.GetPtr() == " << sp.GetPtr() << endl;for (size_t i = 0; i < n; ++i){ape::shared_ptr<Date> copy(sp);}}void test_shared_safe(){ape::shared_ptr<Date> p(new Date);cout << "test_shared_safe: p.GetPtr() == " << p.GetPtr() << endl;const size_t n = 10000;mutex mtx;//线程引用传参即便mtx已经是引用 此处仍然需要使用库函数ref();thread t1(SharePtrFunc, ref(p), n, ref(mtx));thread t2(SharePtrFunc, ref(p), n, ref(mtx));t1.join();t2.join();cout << "p.GetCount(): == " << p.GetCount() << endl;}
类实例化的对象使用时需要手动加锁保护
"锁"的引进
线程引用传参问题
简单理解为 p在传给sp前是需要先调用线程的构造函数的 期间发生了某种动作 使得失去引用属性
4.4整体代码
template<class T>
class shared_ptr
{
public://构造函数shared_ptr(T* ptr):_ptr(ptr), _pcount(new int(1)), _pmtx(new mutex){}void Release(){//上锁_pmtx->lock();//不可释放锁bool deleteFlag = false;if (--(*_pcount) == 0){cout << "delete:" << _ptr << endl;delete _ptr;delete _pcount;//可释放锁deleteFlag = true;}//解锁_pmtx->unlock();//判断并释放锁if (deleteFlag){delete _pmtx;}}//void Release()//{// _pmtx->lock();// bool deleteFlag = false;// if (--(*_pcount) == 0)// {// if (_ptr)// {// //cout << "delete:" << _ptr << endl;// //delete _ptr;//// // 删除器进行删除// _del(_ptr);// }//// delete _pcount;// deleteFlag = true;// }//// _pmtx->unlock();//// if (deleteFlag)// {// delete _pmtx;// }//}void AddCount(){_pmtx->lock();++(*_pcount);_pmtx->unlock();}//拷贝构造shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr), _pcount(sp._pcount), _pmtx(sp._pmtx){AddCount();}//赋值重载//1."自己"给"自己"赋值//不仅仅是p1 = p1 还要考虑p2 = p1 但之前p2就 = p1//2.左 = 右 赋值后 // 左指针指向空间的指针少了一个 左指针的count-- 进一步考虑count--后==0的情况// 右指针指向空间的指针多了一个 右指针的count++shared_ptr<T>& operator=(const shared_ptr<T>& sp){//"自己"给"自己"赋值: "自己"的本质if (_ptr != sp._ptr){//count--Release();_ptr = sp._ptr;_pcount = sp._pcount;_pmtx = sp._pmtx;//count++AddCount();}return *this;}//析构函数~shared_ptr(){Release();}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* GetPtr(){return _ptr;}int GetCount(){return *_pcount;}private:T* _ptr;int* _pcount;mutex* _pmtx;
};void test_shared()
{shared_ptr<int> sp1(new int(1));shared_ptr<int> sp2(sp1);shared_ptr<int> sp3(sp2);shared_ptr<int> sp4(new int(10));sp1 = sp4;sp4 = sp1;sp1 = sp1;sp1 = sp2;
}// 线程安全问题 ///
struct Date
{int _year = 0;int _month = 0;int _day = 0;
};void SharePtrFunc(ape::shared_ptr<Date>& sp, size_t n, mutex& mtx)
{cout << " SharePtrFunc: sp.GetPtr() == " << sp.GetPtr() << endl;cout << " SharePtrFunc: &sp == " << &sp << endl;for (size_t i = 0; i < n; ++i){ape::shared_ptr<Date> copy(sp);mtx.lock();sp->_year++;sp->_day++;sp->_month++;mtx.unlock();}
}void test_shared_safe()
{ape::shared_ptr<Date> p(new Date);cout << "test_shared_safe: p.GetPtr() == " << p.GetPtr() << endl;cout << "test_shared_safe: &p == " << &p << endl;const size_t n = 100000;mutex mtx;//线程引用传参即便p和mtx已经是引用 此处仍然需要使用库函数ref();thread t1(SharePtrFunc, ref(p), n, ref(mtx));thread t2(SharePtrFunc, ref(p), n, ref(mtx));t1.join();t2.join();cout << "p.GetCount(): == " << p.GetCount() << endl;cout << "p->_year == " << p->_year << endl;cout << "p->_month == " << p->_month << endl;cout << "p->_month == " << p->_month << endl;}
5.循环引用问题
5.1问题的引入
5.2分析造成此问题的原因
为了解决此问题 需要引进weaked_ptr 我们需要了解的是
智能指针shared_ptr满足
- 符合RAII思想
- 可以像指针一样使用
- 支持拷贝
智能指针weaked_ptr满足
- 不符合RAII思想
- 可以像指针一样使用
- 辅助解决shared_ptr的循环引用问题
- weaked_ptr可以指向资源,但是不参与管理,不增加引用计数
实际上库里的智能指针远比我们上述讲到的复杂得多 为了便于学习和理解 我们只学习核心框架 需要了解的是 库里所支持的这两个函数
weaked_ptr有自己的count 配合expired来判断所指向资源是否还有被指向的必要 即weaked_ptr可以指向资源,但是不参与管理,不增加shared_ptr引用计数 所以存在于一种所指向资源的计数已经成0 此时expired判断是否失效 若所指向资源失效weaked_ptr就不再指向
5.3weak_ptr的主要代码
template<class T>
class weak_ptr
{
public:weak_ptr():_ptr(nullptr){}weak_ptr(const shared_ptr<T>& sp):_ptr(sp.GetPtr()){}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* GetPtr(){return _ptr;}private:T* _ptr;
};
6.数组对象的删除问题
6.1代码问题
ape::shared_ptr<Date> sparr(new Date[10]);
当使用我们自己模拟实现的简洁版shared_ptr时 上述代码会报错(手动实现Date的析构函数时会报错 不手动实现调用库的析构函数不报错 因为当手动实现了析构函数 Date的空间前会有一个4字节的空间用来存放实例化对象的个数 以便知道调用几次析构函数 但是此时析构函数应该先向前偏移4字节 以便析构时把这4个字节也释放
6.2std::shared_ptr面对此问题的解决方案
1.首先看std::shared_ptr::~shared_ptr
即库里的shared_ptr::~shared_ptr与我们写的析构函数不同之处在于 一个对象在实例化时 他会判断是否接受了参数deleter 如果接收则析构时调用deleter析构 若没有接收deleter则正常析构
2.删除器的传参及使用
销毁对象。但是,以前,根据成员use_count的值,它可能会产生以下副作用:如果use_count大于1(即该对象与其他shared_ptr对象共享其托管对象的所有权):与其共享所有权的其他对象的使用计数将减少1。如果use_coount为1(即,该对象是托管指针的唯一所有者):它所拥有的指针被删除(如果shared_ptr对象是用特殊的deleter构造的,则调用它;否则,函数使用运算符delete)。如果use_count为零(即对象为空),则此析构函数没有副作用。
template<class T>struct DeleteArray{void operator()(T* ptr){cout << "匿名对象DeleteArray<Date>(): " << ptr << endl;delete[] ptr;}};void test_std_shared_deletor(){//template <class U, class D> //shared_ptr (U* p, D del); 带删除器的构造函数std::shared_ptr<Date> sparr1(new Date[10], DeleteArray<Date>());std::shared_ptr<Date> sparr2(new Date[10],[](Date* ptr){cout << "lambda表达式 delete[]: " << ptr << endl;delete[] ptr;});auto deleter = [](Date* ptr){cout << "lambda表达式 delete[]: " << ptr << endl;delete[] ptr;};std::shared_ptr<Date> sparr3(new Date[10], deleter);std::shared_ptr<FILE> spFile(fopen("Test.cpp", "r"),[](FILE* ptr){cout << "lambda表达式 delete[]: " << ptr << endl;fclose(ptr);});}
3.添加封装删除器
7.总结
7.1完整代码
#pragma once#include <mutex>
#include <thread>
#include <memory>namespace ape
{template<class T>class shared_ptr{public://构造函数shared_ptr(T* ptr = nullptr):_ptr(ptr), _pcount(new int(1)), _pmtx(new mutex){}//删除器构造函数template<class D>shared_ptr(T* ptr, D del):_ptr(ptr), _pcount(new int(1)), _pmtx(new mutex), _del(del){}//非删除器Release()/*void Release(){//上锁_pmtx->lock();//不可释放锁bool deleteFlag = false;if (--(*_pcount) == 0){if (_ptr != nullptr){cout << "delete:" << _ptr << endl;delete _ptr;}delete _pcount;//可释放锁deleteFlag = true;}//解锁_pmtx->unlock();//判断并释放锁if (deleteFlag){delete _pmtx;}}*///删除器Release()void Release(){_pmtx->lock();bool deleteFlag = false;if (--(*_pcount) == 0){if (_ptr != nullptr){_del(_ptr);}delete _pcount;deleteFlag = true;}_pmtx->unlock();if (deleteFlag){delete _pmtx;}}void AddCount(){_pmtx->lock();++(*_pcount);_pmtx->unlock();}//拷贝构造shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr), _pcount(sp._pcount), _pmtx(sp._pmtx){AddCount();}//赋值重载//1."自己"给"自己"赋值//不仅仅是p1 = p1 还要考虑p2 = p1 但之前p2就 = p1//2.左 = 右 赋值后 // 左指针指向空间的指针少了一个 左指针的count-- 进一步考虑count--后==0的情况// 右指针指向空间的指针多了一个 右指针的count++shared_ptr<T>& operator=(const shared_ptr<T>& sp){//"自己"给"自己"赋值: "自己"的本质if (_ptr != sp._ptr){//count--Release();_ptr = sp._ptr;_pcount = sp._pcount;_pmtx = sp._pmtx;//count++AddCount();}return *this;}//析构函数~shared_ptr(){Release();}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* GetPtr() const{return _ptr;}int GetCount() const{return *_pcount;}private:T* _ptr;int* _pcount;mutex* _pmtx;//包装器//缺省值处理情况: ape::shared_ptr<Date> sp(new Date);//因为你析构时默认使用删除器 那么遇到没有显示传的要使用缺省值//构造函数传deleter时 可以是仿函数 lambda表达式 函数指针 此处用包装器接收function<void(T*)> _del = [](T* ptr){cout << "lambda表达式 delete: " << ptr << endl;delete ptr;};};void test_shared(){shared_ptr<int> sp1(new int(1));shared_ptr<int> sp2(sp1);shared_ptr<int> sp3(sp2);shared_ptr<int> sp4(new int(10));sp1 = sp4;sp4 = sp1;sp1 = sp1;sp1 = sp2;}// 线程安全问题 ///struct Date{int _year = 0;int _month = 0;int _day = 0;~Date(){}};void SharePtrFunc(ape::shared_ptr<Date>& sp, size_t n, mutex& mtx){cout << " SharePtrFunc: sp.GetPtr() == " << sp.GetPtr() << endl;cout << " SharePtrFunc: &sp == " << &sp << endl;for (size_t i = 0; i < n; ++i){ape::shared_ptr<Date> copy(sp);mtx.lock();sp->_year++;sp->_day++;sp->_month++;mtx.unlock();}}void test_shared_safe(){ape::shared_ptr<Date> p(new Date);cout << "test_shared_safe: p.GetPtr() == " << p.GetPtr() << endl;cout << "test_shared_safe: &p == " << &p << endl;const size_t n = 100000;mutex mtx;//线程引用传参即便p和mtx已经是引用 此处仍然需要使用库函数ref();thread t1(SharePtrFunc, ref(p), n, ref(mtx));thread t2(SharePtrFunc, ref(p), n, ref(mtx));t1.join();t2.join();cout << "p.GetCount(): == " << p.GetCount() << endl;cout << "p->_year == " << p->_year << endl;cout << "p->_month == " << p->_month << endl;cout << "p->_month == " << p->_month << endl;}template<class T>class weak_ptr{public:weak_ptr():_ptr(nullptr){}weak_ptr(const shared_ptr<T>& sp):_ptr(sp.GetPtr()){}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* GetPtr(){return _ptr;}private:T* _ptr;};// 循环引用struct ListNode{/*普通写法ListNode* _next;ListNode* _prev;int _val;*//*shared_ptrape::shared_ptr<ListNode> _next;ape::shared_ptr<ListNode> _prev;int _val;*///weaked_ptrape::weak_ptr<ListNode> _next;ape::weak_ptr<ListNode> _prev;int _val;~ListNode(){cout << "~ListNode()" << endl;}};// 循环引用void test_shared_cycle(){/*常规写法 -- 抛异常场景不适合ListNode* n1 = new ListNode;ListNode* n2 = new ListNode;n1->_next = n2;n2->_prev = n1;delete n1;delete n2;*///智能指针写法ape::shared_ptr<ListNode> n1(new ListNode);ape::shared_ptr<ListNode> n2(new ListNode);//内置类型 = 自定义类型 -- error/*ListNode* _next;ListNode* _prev;int _val;ape::shared_ptr<ListNode> n1(new ListNode);ape::shared_ptr<ListNode> n2(new ListNode);n1->_next = n2;n2->_prev = n1;*//*shared_ptr: 引发循环引用问题ape::shared_ptr<ListNode> _next;ape::shared_ptr<ListNode> _prev;int _val;ape::shared_ptr<ListNode> n1(new ListNode);ape::shared_ptr<ListNode> n2(new ListNode);n1->_next = n2;n2->_prev = n1;*//*weak_ptr: 解决循环引用ape::weak_ptr<ListNode> _next;ape::weak_ptr<ListNode> _prev;int _val;ape::shared_ptr<ListNode> n1(new ListNode);ape::shared_ptr<ListNode> n2(new ListNode);*/cout << "n1.GetCount() == " << n1.GetCount() << endl;cout << "n2.GetCount() == " << n2.GetCount() << endl;n1->_next = n2;n2->_prev = n1;cout << "n1.GetCount() == " << n1.GetCount() << endl;cout << "n2.GetCount() == " << n2.GetCount() << endl;}///定制删除器 -- 可调用对象template<class T>struct DeleteArray{void operator()(T* ptr){cout << "匿名对象DeleteArray<Date>(): " << ptr << endl;delete[] ptr;}};//std库deleter的学习/*void test_std_shared_deletor(){//template <class U, class D> //shared_ptr (U* p, D del); 带删除器的构造函数std::shared_ptr<Date> sparr1(new Date[10], DeleteArray<Date>());std::shared_ptr<Date> sparr2(new Date[10],[](Date* ptr){cout << "lambda表达式 delete[]: " << ptr << endl;delete[] ptr;});auto deleter = [](Date* ptr){cout << "lambda表达式 delete[]: " << ptr << endl;delete[] ptr;};std::shared_ptr<Date> sparr3(new Date[10], deleter);std::shared_ptr<FILE> spFile(fopen("Test.cpp", "r"),[](FILE* ptr){cout << "lambda表达式 delete[]: " << ptr << endl;fclose(ptr);});}*///删除器构造函数/*template<class D>shared_ptr(T* ptr, D del):_ptr(ptr), _pcount(new int(1)), _pmtx(new mutex), _del(del){}*/void test_ape_shared_deleter(){ ape::shared_ptr<Date> sp(new Date);ape::shared_ptr<Date> sparr1(new Date[10], DeleteArray<Date>());ape::shared_ptr<Date> sparr2(new Date[10], [](Date* ptr) {cout << "lambda表达式 delete[]: " << ptr << endl;delete[] ptr;});auto deleter = [](Date* ptr){cout << "lambda表达式 delete[]: " << ptr << endl;delete[] ptr;};ape::shared_ptr<Date> sparr3(new Date[10], deleter);ape::shared_ptr<FILE> spFile(fopen("Test.cpp", "r"),[](FILE* ptr){cout << "lambda表达式 delete[]: " << ptr << endl;fclose(ptr);});}
}
7.2C++11和Boost智能指针的关系
- C++ 98 中产生了第一个智能指针auto_ptr.
- C++ boost给出了更实用的scoped_ptr/shared_ptr/weak_ptr.
- C++ TR1,引入shared_ptr。[TR1不是标准版]
- C++ 11引入了unique_ptr/shared_ptr/weak_ptr。unique_ptr对应boost的scoped_ptr。[实现原理参考boost实现]