智能指针是存储指向动态分配对象指针的类,用于生存期控制;能够确保正确销毁动态分配的内存,防止内存泄露。本博文参考博客https://blog.csdn.net/yusiguyuan/article/details/40628805
(1)智能指针的分类
1.不带引用计数的智能指针auto_ptr 、unique_ptr、 scoped_ptr
2.带引用计数的智能指针shared_ptr强智能指针、weak_ptr弱智能指针;
(2)不带引用计数的智能指针释放内存
1.auto_ptr
它的结构中有两个成员:_ptr和owns,只有获得对引用资源的管理权,才有权再出作用域之后释放资源;实现了拥有权限的指针可以释放资源,但是只能管理一块内存,而实际中,我们设计出一块资源可以由多个指针引用的情况
每次创建类的新对象时,初始化指针并将引用计数置为1;当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数;对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数为减至0,则删除对象),并增加右操作数所指对象的引用计数;调用析构函数时,析构函数减少引用计数(如果引用计数减至0,则删除基础对象)。代码示例如下:
template <typename T>
class Auto_Ptr
{
public:
//构造函数
Auto_Ptr (T *ptr = NULL,bool own = false):_ptr(ptr),owner(own){}
//析构函数
~Auto_Ptr(){if(owner) delete _ptr}
//*运算符重载函数
T& operator *(){return *_ptr;}
const T& operator *(){return *_ptr;}
//->运算符重载函数
T* operator ->(){return _ptr;}
//拷贝构造函数
Auto_Ptr(const Auto_Ptr<T> &src)
{
src.release();
ptr = src._ptr;
}
//改变所有权
void release()const
{
((Auto_Ptr<T>*)this)->owner = false;
}
private:
T *_ptr;
bool owner;
}
void main
{
auto_ptr<A> ptr1(new A());
auto_ptr<B> ptr2 = ptr1;
vector<auto_ptr<int>> vec;//erro
}
当这两行代码执行完后,ptr1已经不再指向A这个对象,并且ptr1=NULL,ptr1对A对象的所有权已经转移给了ptr2,也就不能通过ptr1调用该类的方法。
不能将auto_ptr类型的指针作为STL容器的元素。因为容器避免不了直接拷贝构造和互相赋值。auto_ptr的这种特性会给STL容器的使用造成很大的麻烦。
(3)为什么shared_ptr在多线程下要考虑安全问题?
本次讲解主要以Boost库中的shared_ptr为例,首先先了解一下shared_ptr的结构设计,如下图所示:
现在有以下几行代码
shared_ptr<class> x(new class);
shared_ptr<class> y = x;
第二行代码的执行分为两步:先复制_ptr,再复制ref_count,所以不会发生同时(原子)发生;两步骤如下图所示:
所以当出现在多线程条件下时,
考虑一个简单的场景,有 3 个 shared_ptr<Foo> 对象 x、y、z:
shared_ptr<class> z(new class); // 线程之间共享的 shared_ptr
shared_ptr<class> x; // 线程 A 的局部变量
shared_ptr<class> y(new class); // 线程 B 的局部变量
线程执行顺序如下:A线程执行x = z,并且执行完第一步后切换B进程执行z = y;则图解如下:
从上图中可以看到如果先复制_ptr,再复制ref_count会造成x._ptr成了空悬指针,所以存在线程安全问题;接下来的图解讨论的是如果先进行ref_count复制,再复制_ptr会存在什么安全问题:
从代码执行结果来看应该为X指向了Z引用的资源,class1的引用计数为2;Z指向了Y引用的资源,class2的引用计数为2;所以此种情况正确;
由以上的讨论得出shared_ptr在多线程中必须进行加锁控制,注意安全问题。