shared_ptr的作用如同指针,但又比指针更加安全,甚至还能提供基本的线程安全保证。记录了有多少个shared_ptrs共同指向一个对象(即引用计数),它基本上解决了在使用c++开发过程中不可避免的使用指针而遇到的许多问题,例如:内存泄漏和内存的提前释放,还有由于指针内存申请而产生的异常问题等。shared_ptr指针解决了auto_ptr和一旦最后一个这样的指针被销毁,也就是一旦某个对象的引用计数变为0,这个对象会被自动删除。这在非环形数据结构中防止资源泄露有帮助。
auto_ptr由于它的破坏性复制语义,无法满足标准容器对元素的要求,因而不能放在标准容器中;如果我们希望当容器析构时能自动把它容纳的指针元素所指的对象删除时,通常采用一些间接的方式来实现,显得比较繁琐。boost库中提供了一种新型的智能指针shared_ptr,它解决了在多个指针间共享对象所有权的问题,同时也满足容器对元素的要求,因而可以安 全地放入容器中。
shared_ptr在boost中地位相当重要 。而要想较好的使用shared_ptr来完全取代指针绝非易事。下面简要说说使用shared_ptr需要注意的问题。
1.share_ptr是一个类,它产生的是一个类对象,而不是一个原生的指针对象,但是为了减少类对象与针对对象使用的差异性,所以share_ptr类重载了两种常见的指针操作符: *和->。从而share_ptr与普通指针使用方式一样。简言之,就是share_ptr生成的一个包含类型指针容器对象,它封装了指针对象,对指针对象负全责,包括生成、释放等;
2.share_ptr类重载了许多的构造函数,其中包含无参的构造函数用来创建一个持有空指针的share_ptr对象,但它不等价于持有void*型的指针对象。要想生成一个存储void*型的指针,可以使用share_ptr(void*)构造函数来构造,它相当于一个泛型的指针容器,拥有容纳任意类型的能力,但是将包含void*指针的shared_ptr对象使用转型函数转型为某种类型的指针会使代码不够安全,一般不要使用;
4.share_ptr完美支持标准容器,并且不需要担心资源泄漏。而标准容易在使用指针对象时需要特别的小心,对指针需要额外的管理。
下面就用实例证明一下:
#include<iostream>
#include <memory>
#include<stdlib.h>
using namespace std;
struct Node
{
shared_ptr<Node> _next;
shared_ptr<Node> _prev;
int _data;
};
int main()
{
shared_ptr<Node> p1(new Node);
shared_ptr<Node> p2(new Node);
cout << "p1->Count:" << p1.use_count() << endl;
cout << "p2->Count:" << p2.use_count() << endl;
p1->_next = p2;
p2->_prev = p1;
cout << "p1->Count:" << p1.use_count() << endl;
cout << "p2->Count:" << p2.use_count() << endl;
system("pause");
return 0;
}
但是析构函数时就会出现问题,调用Delete函数后,p1,p2的引用计数本应该变为0;但是引用计数还是2,这是怎么回事呢? 答案是:循环引用,调用析构函数时引用计数减为1,但是p2的析构依赖于p1,同理p1的析构依赖于p2
,因此就会造成循环引用问题。那么该怎么解决呢? 那就是weak_ptr(),weak_ptr()完全可以将循环引用问题解决.
#include<iostream>
#include<memory>
#include<stdlib.h>
using namespace std;
struct Node
{
weak_ptr<Node> _next;
weak_ptr<Node> _prev;
int value;
};
int main()
{
shared_ptr<Node> p1(new Node);
shared_ptr<Node> p2(new Node);
p1->_next = p2;
p2->_prev = p1;
system("pause");
return 0;
}
//下面是boost库下的weak_ptr类
namespace boost
{
template<typename T> class weak_ptr
{
public:
template <typename Y>
weak_ptr(const shared_ptr<Y>& r);
weak_ptr(const weak_ptr& r);
~weak_ptr();
T* get() const;
bool expired() const;
shared_ptr<T> lock() const;
};
}
可以看到,boost::weak_ptr必须从一个boost::share_ptr或另一个boost::weak_ptr转换而来,这也说明,进行该对象的内存管理的是那个强引用的 boost::share_ptr。
boost::weak_ptr只是提供了对管理对象的一个访问手段。
boost::weak_ptr除了对所管理对象的基本访问功能(通过get()函数)外,还有两个常用的功能函数:
1. expired() 用于检测所管理的对象是否已经释放;
2. lock() 用于获取所管理的对象的强引用指针。
通过boost::weak_ptr来打破循环引用 ,由于弱引用不更改引用计数,类似普通指针,只要把循环引用的一方使用弱引用,即可解除循环引用
定制删除器;
定制删除器;
定制删除器
为什么在shared_ptr中介绍删除器呢?(因为auto_ptr存在缺陷,scoped_ptr功能不全)
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <memory>
using namespace std;
// 定置的删除器仿函数
template<class T>//注意这里有模版
struct Del
{
void operator()(const T* ptr)
{
delete ptr;
cout << "delete" << endl;
}
};
struct Free//注意这里没有模版
{
void operator()(void* ptr)
{
free(ptr);
cout << "free" << endl;
}
};
struct Fclose
{
void operator()(void* ptr)
{
fclose((FILE*)ptr);
cout << "fclose" << endl;
}
};
template<class T, class Deleter = Del<T>>
class SharedPtr
{
public:
SharedPtr(T* ptr, Deleter del)
:_ptr(ptr)
, _pCount(new long(1))
, _del(del)
{}
SharedPtr(T* ptr)
:_ptr(ptr)
, _pCount(new long(1))
{}
~SharedPtr()
{
_Release();
}
protected:
void _Release()
{
if (--*_pCount == 0)
{
_del(_ptr);
delete _pCount;
}
}
private:
T* _ptr;
long* _pCount;
Deleter _del;
};
void Test()
{
// 定制删除器
SharedPtr<int> p1(new int(1));
// 定制删除器和分配器
SharedPtr<int, Free>sp2((int*)malloc(sizeof(int)* 10), Free());
}
int main()
{
Test();
system("pause");
return 0;
}