目录
一、先来看一下什么是智能指针
二、 auto_ptr
1、C++98版本
2、C++11的auto_ptr
三、boost 库中的智能指针
1. scoped_ptr
四、C++11中新提供的智能指针
unique_ptr
weak_ptr弱指针
智能: 可以看到我们在申请资源之后并没有进行手动释放但是却没有内存的泄漏
指针: 他明明是个对象,但用起来却像指针一样
可以发现,智能指针是很神奇的,下面让我们来看看他是怎样做到这些的吧
原理如下:类中含有两个成员
模拟:
C++98有着很大的缺陷,主要就是拥有权的问题
当拥有权转移之后,原来的还能去访问到数据,这显然是一种藕断丝连的现象
这就导致了一种错觉,仿佛转移之后还拥有着使用权
我们来看看C++11对这种现象是怎么处理的
我们可以发现C++11就改进了这一点,不存在藕断丝连这一说了
这个指针是个局部智能指针,就像个“貔貅”一样,只进不出,不允许拥有权的转移,构造拷贝都无法进行,底层是一个普通的指针
模拟实现:
template
class scoped_ptr
{
public:
scoped_ptr(T* p = 0) : px(p)
{
}
~scoped_ptr()
{
//检查释放
//boost::checked_delete( px );
delete px;
}
public:
T& operator*() const
{
return *px;
}
T* operator->() const
{
return px;
}
T* get() const
{
return px;
}
public:
typedef scoped_ptr this_type;
void reset(T* p = 0)
{
//构造一个无名临时对象
this_type(p).swap(*this);
//this_type(p) ==> scoped_ptr(p);
}
void swap(scoped_ptr& b) // never throws
{
T* tmp = b.px;
b.px = px;
px = tmp;
}
private:
scoped_ptr(scoped_ptr const&);
scoped_ptr& operator=(scoped_ptr const&); //私有的设置,不允许拷贝,赋值
void operator==(scoped_ptr const&) const;
void operator!=(scoped_ptr const&) const;
private:
T* px;
};
底层原理图:内部是有一个引用计数器
引用计数器和之前学过的写实拷贝的地方实现原理很相似
功能: 允许拷贝,赋值 还可以输出有几个引用
模拟实现:
实现这个shared_ptr 智能指针 需要有四个类
1. class shared_ptr类
#include"shared_count.h"
namespace zyf
{
template
class shared_ptr
{
typedef shared_ptr this_type;
public:
shared_ptr():px(0)
{}
template
shared_ptr(Y* p) : px(p), pn(p)
{
#ifdef DISPLAY
cout << "Created shread_ptr obj." << endl;
#endif
}
~shared_ptr()
{
#ifdef DISPLAY
cout << "Free shread_ptr obj." << endl;
#endif
}
shared_ptr& operator=(const shared_ptr& r)
{
this_type(r).swap(*this);
return *this;
}
shared_ptr(const shared_ptr &r):px(r.px),pn(r.pn)
{}
public:
long use_count()const
{
return pn.use_count();
}
public:
T& operator*()const
{
return *px;
}
public:
void swap(shared_ptr& other)
{
std::swap(px, other.px);
pn.swap(other.pn);
}
private:
T* px;
shared_count pn;//引用计数器对象
};
}
2. shared_count 类
//引用计数器类
class shared_count
{
public:
shared_count():pi_(0)
{}
template
shared_count(Y *p):pi_(0)
{
pi_ = new sp_counted_impl(p);//多态
#ifdef DISPLAY
cout << "Created shread_count obj." << endl;
#endif
}
~shared_count()
{
#ifdef DISPLAY
cout << "Free shread_count obj." << endl;
#endif
if (pi_)
pi_->release();
}
shared_count(const shared_count& r):pi_(r.pi_)
{
if (pi_ != 0)
pi_->add_ref_copy();
}
public:
long use_count()const
{
if (pi_ == nullptr)
return 0;
return pi_->use_count();
}
public:
void swap(shared_count& r)
{
sp_counted_base* tmp = r.pi_;
r.pi_ = pi_;
pi_ = tmp;
}
private:
sp_counted_base* pi_;
};
3. sp_counted_base类
class sp_counted_base
{
public:
sp_counted_base() :use_count_(1)
{
#ifdef DISPLAY
cout << "Created sp_counted_base obj" << endl;
#endif
}
virtual ~sp_counted_base()
{
#ifdef DISPLAY
cout << "Free sp_counted_base obj" << endl;
#endif
}
public:
void add_ref_copy()
{
use_count_++;
}
void release()
{
if (--use_count_ == 0)
{
delete this;
}
}
long use_count()const
{
return use_count_;
}
private:
long use_count_;
};
4.sp_counted_impl类 ,这个类是继承 sp_sounted_base 类的
#include"sp_counted_base.h"
template
class sp_counted_impl : public sp_counted_base
{
public:
sp_counted_impl(X *px):px_(px)
{
#ifdef DISPLAY
cout << "Created sp_sounted_impl obj." << endl;
#endif
}
~sp_counted_impl()
{
delete px_;
#ifdef DISPLAY
cout << "Free sp_sounted_impl obj." << endl;
#endif
}
private:
X* px_;
};
和scoped_ptr 一样 ,不允许拥有权的转移,和“貔貅”一样
std::shared_ptr的线程安全问题
这里的线程不安全问题和之前的黄牛抢票现象是一样的:
这里我们用一个双向列表的例子来举例说明什么是循环引用
我们先只创建两个节点类型,观察一下情况会是什么样子的。
我们发现,构造和析构函数能够正确的被调用,没有出现问题
这时候我们再来将这两个节点连接起来,观察一下会是怎么样的
我们很神奇的发现,不连还好,一连就出问题,析构函数没有被调用,这是什么情况呢?我们来探究一下
在函数调用结束后释放node1和node2智能指针所指向的资源的时候会给两个资源的计数器都减减,但是也只是减为1,两个节点内部的指针相互指向,到最后谁都释放不了。
根本原因:
节点相互连接赋值的时候,因为内部的prev和 next 和节点的 node类型是一种类型,导致引用计数器会增加,
解决这里的办法就是使用weak_ptr
weak_ptr 是为配合 shared ptr 而引入的一种智能指针,它更像是 shared ptr 的一个助手而不是智能指针,因为它不具有普通指针的行为,没有重载 operator*和->。它的最大作用在于协助 shared ptr 工作,像旁观者那样观测资源的使用情况。
上面我们知道了,使用weak_ptr 可以不增加引用计数
使用了weak_ptr之后我们再来看看会是怎么样的
只要共享性智能指针对象的释放方式不能满足我们的要求,我们就可以取定制一个
1. 遇到数组类型的空间
这时候可以定制一个删除器
这时候就解决问题了
2. 还可以解决不是new 出来的空间问题
比如是malloc出来的,这时候应该用free去对应
其他问题也可以: 比如是文件描述符: socketfd 、 newsockfd 、fp