一、shared_ptr的循环引用问题
在上篇文章中介绍了智能指针的基本知识,我们感觉shared_ptr好像特别厉害的样子,其
实它就是特别厉害,只是它有循环引用的问题,看下边的代码:
struct Node
{
int _data;
shared_ptr<Node> _next;
shared_ptr<Node> _prev;
};
void test()
{
shared_ptr<Node> sp1(new Node);
shared_ptr<Node> sp2(new Node);
cout << sp1.use_count() << endl;
cout << sp2.use_count() << endl;
sp1->_next = sp2;
sp2->_prev = sp1;
cout << sp1.use_count() << endl;
cout << sp2.use_count() << endl;
}
上边的程序最终会输出1 1 2 2(数字之间以回车间隔),程序结束时,sp1和 sp2并不
会被析构(引用计数未被变为0)。下边图解。
注意:上边画绿线部分提到sp1和sp2都被释放,并不是空间真正被释放,而是相应空间
的引用计数减1.
那么,该如何解决这个问题呢?
我们引入了weak_ptr弱指针,它是用来辅助shared_ptr,并不可以单独使用。将一
个weak_ptr绑定到一个shared_ptr,并不会改变shared_ptr的引用计数,一旦最后一个
指向对象的shared_ptr被销毁,对象就会被销毁,即使有weak_ptr指向对象,对象还是
会被释放。所以,上边定义的Node结构体改成下边这样,就能解决循环引用的问题。
struct Node
{
int _data;
weak_ptr<Node> _next;
weak_ptr<Node> _prev;
};
二、定制删除器
智能指针的引入是为了解决因程序执行流的改变而引起的内存泄漏的问题。在构造函数
中开辟和初始化,在析构函数中完成清理工作。智能指针是模板,所以就应该可以解决
各种类型的内存泄漏问题。在构造函数中打开的文件,在析构函数中完成文件的关闭,
在构造函数中malloc开辟的空间,在析构函数中用free释放。所以,我们必须为每一种
类型写出对应的内存释放的函数。
通过仿函数(重载())来实现。下边展示定制删除器的模拟实现:
#include<iostream>
using namespace std;
#include<boost/shared_ptr.hpp>
template<typename T,typename D = DefaultDelete>
class SharedPtr
{
public:
SharedPtr(T* ptr)
:_ptr(ptr)
,_pCount(new int (1))
,_del(D())
{}
~SharedPtr()
{
if (--*_pCount == 0)
{
//delete _ptr;
_del(_ptr);
delete _pCount;
}
}
SharedPtr(const SharedPtr<T,D>& sp)
{
_ptr = sp._ptr;
_pCount = sp._pCount;
++*_pCount;
}
SharedPtr<T,D>& operator=(const SharedPtr<T,D>& sp)
{
if (_ptr != sp._ptr)
{
if (_ptr != nullptr)
{
if (--*_pCount == 0)
{
//delete _ptr;
_del(_ptr);
delete _pCount;
}
}
else
{
_ptr = sp._ptr;
_pCount = sp._pCount;
++(*_pCount);
}
}
return *this;
}
private:
T* _ptr;
int* _pCount;
D _del;
};
//默认删除器
struct DefaultDelete
{
void operator() (void* ptr)
{
delete ptr;
}
};
struct Fclose
{
void operator() (void* ptr)
{
fclose((FILE* )ptr);
}
};
struct Free
{
void operator() (void* ptr)
{
free(ptr);
}
};
void test_del()
{
SharedPtr<int>sp1(new int (1));
SharedPtr<FILE,Fclose>sp2(fopen("hello.txt","w"));
}
当智能指针管理的是文件时,出作用域时就会调用关闭文件的函数。这就是所谓的定制
删除器。