从零开始写STL - 智能指针
从零开始写STL - 智能指针
- 智能指针的分类及其特点:
scoped_ptr:初始化获得资源控制权,在作用域结束释放资源
shared_ptr: 引用计数来控制共享资源,最后一个资源的引用被释放的时候会析构
unique_ptr: 只能有一个持有资源引用的对象
weak_ptr:eak_ptr也维护了一个引用计数,跟shared_ptr维护的引用计数或互不干扰,或相互协同。weak_ptr的指针会在weak_ptr维护的引用计数上加一,而shared_ptr会在shared_ptr维护的引用计数上加一,这样在循环引用时,就会因为对不同引用的判断的不同,使最终决定是否释放空间的结果也不相同。
定制化删除器
在某些特定情况下,我们需要定制化的对象清理行为
比如在服务器端保持一个 Session的列表,由于要保证Session的动态销毁,应该使用unordered_map<weak_ptr_to_session > 来高效检索对象(如果使用shared_ptr 会导致对象生存期过长(直到服务器端销毁map)),此时我们在shared_ptr_to_session里面应该定制化这样的清理操作:在清理自身管理的内存对象的同时,应该对哈希表中删除对应的weak_ptr(否则哈希表中只添加不删除也是一种内存泄露)。
如何实现
- 默认模板类型 + std::bind函数
template<typename T>
class Deleter {
public:
typedef std::function<void(T*)> DeleteFunc;
//默认删除
Deleter(): func(std::bind(&Deleter::defaultfunc,this, std::placeholders::_1)){}
//定制化
Deleter(DeleteFunc df) :func(df) {}
void operator()(T* p) const {
func(p);
};
private:
void defaultfunc(T* ptr) {
ptr->~T();
}
DeleteFunc func;
};
unique_ptr
最简单的智能指针类型,注意在所有权转移时将对应的原对象析构即可。
//默认使用delete 来销毁类型
template<typename T, typename Deleter = ministl::Deleter<T>>
class unique_ptr {
public:
typedef T* pointer;
unique_ptr() :data(nullptr), delfunc() {}
unique_ptr(const unique_ptr<T>&) = delete;
unique_ptr(unique_ptr<T>&& rhs) :delfunc(rhs.delfunc), data(rhs.data) {
rhs.data = nullptr;
}
unique_ptr<T>& operator=(const unique_ptr<T>& rhs) = delete;
~unique_ptr() {
delfunc(data);
}
void reset() {
delfunc(data);
data = nullptr;
}
void reset(T* _data) {
delfunc(data);
data = _data;
}
pointer release() noexcept {
pointer ret = data;
data = nullptr;
return ret;
}
pointer get() const noexcept {
return data;
}
T& operator * () const noexcept{
return *data;
}
T* operator ->() const noexcept{
return &*this;
}
private:
Deleter delfunc;
T* data;
};
shared_ptr
实现的几个问题:
- 引用计数如何保存?
每个shared_ptr 维护一个指针,指向共享引用计数节点,节点中保存强引用和弱引用的数量,强引用用来控制管理对象的生命周期,弱引用用来控制节点的生存周期。(节点的回收在弱引用计数为零时)
注意shared_ptr对象持有的是一个强引用和一个弱引用(隐式存在),因为要同时控制对象生存周期和节点生存周期。
- 在哪里进行对象析构?
在引用计数为0时
std::shared_ptr is a smart pointer that retains shared ownership of an object through a pointer. Several shared_ptr objects may own the same object. The object is destroyed and its memory deallocated when either of the following happens
the last remaining shared_ptr owning the object is destroyed;
the last remaining shared_ptr owning the object is assigned another pointer via operator= or reset().The object is destroyed using delete-expression or a custom deleter that is supplied to shared_ptr during construction.
A shared_ptr can share ownership of an object while storing a pointer to another object. This feature can be used to point to member objects while owning the object they belong to. The stored pointer is the one accessed by get(), the dereference and the comparison operators. The managed pointer is the one passed to the deleter when use count reaches zero.A shared_ptr may also own no objects, in which case it is called empty (an empty shared_ptr may have a non-null stored pointer if the aliasing constructor was used to create it).All specializations of shared_ptr meet the requirements of CopyConstructible, CopyAssignable, and LessThanComparable and are contextually convertible to bool.All member functions (including copy constructor and copy assignment) can be called by multiple threads on different instances of shared_ptr without additional synchronization even if these instances are copies and share ownership of the same object. If multiple threads of execution access the same shared_ptr without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur; the shared_ptr overloads of atomic functions can be used to prevent the data race.
template<typename T, typename Deleter = ministl::Deleter<T>>
class Data_node {
public:
Data_node() :refcnt(0), weakcnt(0), data(nullptr) {}
Data_node(T* _data) : refcnt(1), weakcnt(1), data(_data) {}
~Data_node() = default;
void incref() {
refcnt++;
}
void decref() {
assert(refcnt != 0);
refcnt--;
}
T* getData() {
return data;
}
int getRefCnt() {
return refcnt;
}
//for weak_ptr
void incweak() {
weakcnt++;
}
void desweak() {
assert(weakcnt != 0);
weakcnt--;
}
void increment() {
refcnt++, weakcnt++;
}
void decrement() {
refcnt--, weakcnt--;
check();
}
// if we need to delete data or delete ptr_ndoe
void check() {
if (refcnt == 0) {
delfunc(data);
data = nullptr;
}
if (weakcnt == 0) {
delete this;
}
}
typedef std::atomic<int> atomic_int;
Deleter delfunc;
atomic_int refcnt;
atomic_int weakcnt;
T* data;
};
template<class T, typename Deleter = ministl::Deleter<T>>
class shared_ptr
{
typedef Data_node<T, Deleter> node;
typedef Data_node<T, Deleter>* node_ptr;
typedef shared_ptr<T, Deleter> self;
private:
node_ptr ptr;
public:
shared_ptr()noexcept : ptr(nullptr)
{
}
shared_ptr(const self& x)
{
if (this->ptr != x.ptr)
{
if (ptr != nullptr)
ptr->decrement();
if (x.ptr != nullptr)
x.ptr->increment();
ptr = x.ptr;
}
}
explicit shared_ptr(T* data_ptr)
{
ptr = new node(data_ptr);
}
shared_ptr(const node_ptr _ptr) {
ptr = _ptr;
ptr->increment();
}
~shared_ptr()
{
if (ptr != nullptr)
{
ptr->decrement();
ptr = nullptr;
}
}
operator bool() {
return ptr != nullptr && ptr->refcnt != 0;
}
self& operator= (const self& x) noexcept
{
if (this->ptr == x.ptr)
return *this;
if (ptr != nullptr)
ptr->decrement();
if (x.ptr != nullptr)
x.ptr->increment();
ptr = x.ptr;
return *this;
}
T& operator*()
{
return *(ptr->data);
}
size_t use_count()
{
if (ptr == nullptr) {
return 0;
}
return ptr->count();
}
bool unique() const noexcept
{
return use_count() == 1;
}
void swap(shared_ptr& x) noexcept
{
std::swap(x.ptr, ptr);
}
void reset() noexcept
{
if (ptr == nullptr)return;
ptr->decrement();
ptr = new node();
}
//这里使用默认模板类型初始化删除器存在一个问题
//无法动态改变智能指针类型
//TODO 将删除器作为function放在指针类中
/*template <class U>
void reset(U* p)
{
if (ptr == nullptr)return;
ptr->decrement();
ptr = new Data_node<U>(p);
}*/
T* get() noexcept
{
return ptr->ptr;
}
node_ptr get_node() noexcept {
return ptr;
}
};
template<class T, class...Args>
shared_ptr<T> make_shared(Args... args)
{
return shared_ptr<T>(new T(std::forward<Args>(args)...));
}
weak_ptr
- std::weak_ptr is a smart pointer that holds a non-owning ("weak") reference to an object that is managed by std::shared_ptr. It must be converted to std::shared_ptr in order to access the referenced object.std::weak_ptr models temporary ownership: when an object needs to be accessed only if it exists, and it may be deleted at any time by someone else, std::weak_ptr is used to track the object, and it is converted to std::shared_ptr to assume temporary ownership. If the original std::shared_ptr is destroyed at this time, the object's lifetime is extended until the temporary std::shared_ptr is destroyed as well.In addition, std::weak_ptr is used to break circular references of std::shared_ptr.
解决循环计数,注意lock函数
template<class T, typename Deleter = ministl::Deleter<T>>
class weak_ptr {
typedef Data_node<T, Deleter> node;
typedef Data_node<T, Deleter>* node_ptr;
typedef shared_ptr<T, Deleter> shared;
typedef weak_ptr<T> self;
public:
weak_ptr() :ptr(nullptr) {
}
weak_ptr(const shared& rhs) {
ptr = rhs.ptr;
if (rhs.ptr)
ptr->incweak();
}
weak_ptr(const weak_ptr& r) noexcept {
ptr = r.ptr;
if (r.ptr)
ptr->incweak();
}
weak_ptr(weak_ptr&& r) noexcept {
ptr = r.ptr, r.ptr = nullptr;
}
self& operator=(const self& rhs) {
if (ptr != nullptr) {
ptr->desweak();
}
if (rhs.ptr != nullptr) {
rhs.ptr->incweak();
}
ptr = rhs.ptr;
return *this;
}
self& operator=(shared& rhs) {
auto _ptr = rhs.get_node();
if (ptr != nullptr) {
ptr->desweak();
}
if (_ptr != nullptr) {
_ptr->incweak();
}
ptr = _ptr;
return *this;
}
~weak_ptr() {
if (ptr != nullptr)
ptr->desweak();
}
int use_count() {
if (ptr == nullptr)
return 0;
return ptr->refcnt;
}
shared lock() {
return expired() ? shared_ptr<T>() : shared_ptr<T>(ptr);
}
bool expired() const noexcept {
return ptr == nullptr || ptr->refcnt == 0;
}
private:
node_ptr ptr;
};
从零开始写STL - 智能指针的更多相关文章
- 从零开始写STL—栈和队列
从零开始写STL-栈和队列 适配器模式 意图:将一个类的接口转换成客户希望的另外一个接口.适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 主要解决:主要解决在软件系统中,常常要将 ...
- STL 智能指针
转自: https://blog.csdn.net/k346k346/article/details/81478223 STL一共给我们提供了四种智能指针:auto_ptr.unique_ptr.sh ...
- 从零开始写STL—容器—vector
从0开始写STL-容器-vector vector又称为动态数组,那么动态体现在哪里?vector和一般的数组又有什么区别?vector中各个函数的实现原理是怎样的,我们怎样使用会更高效? 以上内容我 ...
- 从零开始写STL—模板元编程之any
any class any; (since C++17) The class any describes a type-safe container for single values of any ...
- 从零开始写STL—functional
function C++11 将任意类型的可调用(Callable)对象与函数调用的特征封装到一起. 这里的类是对函数策略的封装,将函数的性质抽象成组件,便于和algorithm库配合使用 基本运算符 ...
- 从零开始写STL—模板元编程之tuple
tuple Class template std::tuple is a fixed-size collection of heterogeneous values. It is a generali ...
- 从零开始写STL—哈希表
static const int _stl_num_primes = 28; template<typename T, typename Hash = xhash<T>> cl ...
- 从零开始写STL—set/map
这一部分只要把搜索树中暴露的接口封装一下,做一些改动. set源码剖析 template<typename T> class set { public: typedef T key_typ ...
- c++智能指针(unique_ptr 、shared_ptr、weak_ptr、auto_ptr)
一.前序 什么是智能指针? ——是一个类,用来存储指针(指向动态分配对象也就是堆中对象的的指针). c++的内存管理是让很多人头疼的事,当我们写一个new语句时,一般就会立即把delete语句直接也写 ...
随机推荐
- 设计 REST API 的13个最佳实践
写在前面 之所以翻译这篇文章,是因为自从成为一名前端码农之后,调接口这件事情就成为了家常便饭,并且,还伴随着无数的争论与无奈.编写友好的 restful api 不论对于你的同事,还是将来作为第三方服 ...
- 位bit,字节byte,K,M,G(转)
字节是由8个位所组成,可代表一个字符(A~Z).数字(0~9).或符号(,.?!%&+-*/),是内存储存数据的基本单位.1 byte = 8 bit 1 KB = 1024 bytes1 ...
- 自定义Jquery分页插件
/** * 功能说明:jPager 分页插件 * 参数说明:pages:[] 分页的控件个数 @id:显示分页的div ID,@showSelectPage: 是否显示当前分页的条目过滤下拉框 * @ ...
- Android(java)学习笔记167:横竖屏切换时Activity的生命周期
1.横竖屏切换的生命周期 默认情况下横竖屏切换,先销毁再创建 2.有的时候,默认情况下的横竖屏切换(先销毁再创建),对应用户体验是不好的,比如是手机游戏横竖屏切换对游戏体验非常不好,下面两种方 ...
- linux centos 中目录结构的含义
文件夹的含义 文件夹路径 含义 / 所有内容的开始 /root 系统管理员目录 /bin 缺省的liunx工具,就是存储命令的目录 环境变量等等 /etc 系统的配置 配置文件的存 ...
- 1.3 jieba中文处理+安装
第一次接触这个工具,是在研一上学期的一门课里.由于要做课程设计论文,我当时选择做中文分词处理,自然而然就接触到这个工具了. 但是呢,由于研究生研究方向与NLP无关,也就没有深入的研究过. 现在由于工作 ...
- linux之awk命令
一.awk的内置参数 $0:表示整个当前行 $1:每行第一个字段 $2:每行第二个字段 $n:每行第n个字段 awk的参数:分隔符 -F separator 设定分隔符(默认为空格) 打印单个字段: ...
- uva1615 Highway
画图,每个给出点都有对应区间:先sort,再尽量靠右选:很常见的套路了..//注意不要越界(0,L) struct Q //复习结构{ double l,r; Q(double _l,double _ ...
- activitmq+keepalived+nfs 非zk的高可用集群构建
nfs 192.168.10.32 maast 192.168.10.4 savel 192.168.10.31 应对这个需求既要高可用又要消息延迟,只能使用变态方式实现 nfs部署 #yum ins ...
- 【转】解决WPF图片模糊最佳方法(绑定PixelWidth与PixelHeight)
解决WPF图片模糊最佳方法(绑定PixelWidth与PixelHeight) 转载自:http://www.360doc.com/content/13/1126/09/10504424_332211 ...