共享指针,即多个指针指向同一个内存;
具体实现方式是采用的引用计数,即这块地址上每多一个指针指向他,计数加一;
为什么要采取引用计数呢?我是这样理解的:
因为有多个指针指向了同一个内存,如果提前释放了这个内存,那其他指向这个地址的指针则会成为野指针?如果所有的指针都不指向这个地址,这块地址还没被释放,就会被造成内存泄漏。。。所以采用计数的方式来判断这个地址上还有多少个指针,当最后一个指针不再指向这块内存的时候,就释放掉这块内存。
面试问题:这个计数是在哪计的,是每一个共享指针都有一个计数,还是一个内存有一个?。。。。。当然,按照上面的理解应该是每一块内存有一个计数器。那这个计数的变量储存在哪?
先看一下共享指针的一个简单实现:
template
class SharedPtr
{
public:
SharedPtr(T *ptr) : _ptr(ptr), _pCount(new int(1)) {}
SharedPtr(const SharedPtr &ap) : _ptr(ap._ptr), _pCount(ap._pCount)
{
++(*_pCount);
}
SharedPtr &operator=(const SharedPtr &ap)
{
if (_ptr != ap._ptr)
{
if (--(*_pCount) == 0)
{
delete _ptr;
delete _pCount;
}
_ptr = ap._ptr;
_pCount = ap._pCount;
++(*_pCount);
}
return *this;
}
T &operator*()
{
return *_ptr;
}
T *operator->()
{
return _ptr;
}
protected:
T *_ptr;
int *_pCount;
};
实际共享指针封装了两个变量:
protected:
T *_ptr;
int *_pCount;
一个是普通指针,一个是计数的指针。也就是说,每个共享指针通过pCount这个指针找到他指向内存的指针计数;而这个计数变量在哪?
SharedPtr(T *ptr) : _ptr(ptr), _pCount(new int(1)) {}
可以看到,计数是在使用普通指针创建共享指针的时候初始化的。
//使用共享指针创建共享指针
SharedPtr(const SharedPtr &ap) : _ptr(ap._ptr), _pCount(ap._pCount)
{
++(*_pCount);
}
//使用共享指针对另一个共享指针赋值
SharedPtr &operator=(const SharedPtr &ap)
{
if (_ptr != ap._ptr)
{
if (--(*_pCount) == 0)
{
delete _ptr;
delete _pCount;
}
_ptr = ap._ptr;
_pCount = ap._pCount;
++(*_pCount);
}
return *this;
}
在使用共享指针创建共享指针时,都是在之前的引用计数上加一。
因此,答案就是:不是每个内存上存放一个引用计数,也不是每个共享指针存放一个引用计数。而是每次使用普通指针来创建共享指针的时候增加一个引用计数。。。。也就是说,同一个地址可以有多个不同引用计数。。。
例子:
用一个普通指针初始化两个共享指针:
#include
#include
using namespace std;
int main()
{
int *p = new int;
*p = 5;
shared_ptr s_ptr(p);//s_ptr指向了这块地址,pCount = 1
shared_ptr s_ptr1 = s_ptr;//s_ptr1也指向了这块地址,pCount = 2
shared_ptr s_ptr2(p);//s_ptr2也指向了这块地址,不过重新创建了引用计数,pCount1 = 1
cout<< " 初始化后的状态" <
输出结果:
初始化后的状态
p:0x1faa50 value:5
s_ptr :0x1faa50 count:2 value:5
s_ptr1:0x1faa50 count:2 value:5
s_ptr2:0x1faa50 count:1 value:5
可以看到s_ptr2的引用计数为1,不同于s_ptr1。
因此这样也存在风险:如果s_ptr2计数为0,则会执行delete p操作,释放掉内存。这样s_ptr等指针也就指向的内容也就释放了?
加一个例子试一试:
#include
#include
using namespace std;
int main()
{
int *p = new int;
*p = 5;
shared_ptr s_ptr(p);//s_ptr指向了这块地址,pCount = 1
shared_ptr s_ptr1 = s_ptr;//s_ptr1也指向了这块地址,pCount = 2
shared_ptr s_ptr2(p);//s_ptr2也指向了这块地址,不过重新创建了引用计数,pCount1 = 1
cout<< " 初始化后的状态" < s_ptr3(new int(10));
s_ptr2 = s_ptr3; //当前s_ptr2的引用计数为1,
//减少了这次使用引用计数变为0,
//s_ptr2执行 了 delete p;释放了p之前指向的内存
cout<
实际输出结果:
初始化后的状态
p:0x10daa50 value:5
s_ptr :0x10daa50 count:2 value:5
s_ptr1:0x10daa50 count:2 value:5
s_ptr2:0x10daa50 count:1 value:5
=========================================
s_ptr2改变指向后的状态
p:0x10daa50 value:17672824
s_ptr :0x10daa50 count:2 value:17672824
s_ptr1:0x10daa50 count:2 value:17672824
s_ptr2:0x10daa90 count:2 value:10
可以看见之前存储的变量值5确实被释放了。