文章目录
- 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_ptr
ape::shared_ptr<ListNode> _next;
ape::shared_ptr<ListNode> _prev;
int _val;
*/
//weaked_ptr
ape::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实现]