C++ (STL BOOST) 智能指针的一些解析,包括智能指针的原理,智能指针的一些使用。
只有了解了原理才能真正用对他们。现在blog的文章几乎就是一把抄。郁闷
C++里面内存分配一直是大问题。new了何时delete就是一个关键问题。
单对单匹配的new和delete基本上没有问题。
比如
void func()
{
p=new xx
delete p
}
又或者
p=new p
queue.push(p);
-->
p=queue.pop();
delete p
这种一分配,一释放的我建议不用智能指针。因为指针的控制是非常明确。如果不熟悉智能指针,自己反而容易糊涂。
需要用到智能指针的情况基本属于一处new 多处使用。
比如
main()
{
T* p=new T;
newthread(threadfunc,p)
newthread(threadfunc,p)
newthread(threadfunc,p)
}
threadfunc(T* p)
{
do_dosomething();
if(p is last ref object)
{
delete p;
}
}
多个地方使用了同一个变量,当p是最后一个引用这个变量的时候,就需要删除这个p再退出,这时候用智能指针就比较合适了,注意要使用合适的智能指针,
智能指针也有许多种,后续会分析说一些常用的的几种。
智能指针的原理:
智能指针的基本原理就是将指针通过对象去管理,delete的操作利用析构函数执行,而析构函数的调用是根据这个对象的作用域来确定的,离开了作用域,析构函数被调用。delete的操作也将被执行。
最简单的智能指针如下
template
class mptr
{
public:
mptr(T* pt)
{
_pt=pt;
};
~mprt()
{
delete _pt;
}
private:
T* _pt;
}
测试如下
void sampleFunc()
{
printf("Enter sampleFunc\n");
mptr
printf("Leave sampleFunc\n");
}
在Leave sampleFunc以后。由于mptr类型的对象ptr离开了作用域,自动析构。调用了析构函数。delete了 Sample对象。
原理就是这么简单。但是智能指针有很多种。只有了解了他的实现。才能准确使用。否则都不知道怎么崩溃的。
说明常用的智能指针的时候要理解一个概念。指针所有权。也就是这个指针现在属于哪个智能指针对象(即智能指针对象是否能操作这个指针指向的对象)
第一种 std::auto_ptr
auto_ptr是所有权转移的智能指针,也就是同一时刻只有一个智能指针对象对原对象拥有所有权。
因此如下代码是绝对错误的
std::auto_ptr
ptr2=ptr;
ptr->Show();
此时的ptr已经是空指针了。因为ptr2=ptr已经将Sample对象的所有权转移给了ptr2
前面类似多次newthread传递autoptr也不行。在传递第一个createthread的时候拷贝构造函数已经将所有权转移给了这个对象。原来的对象成了空指针,后续的newthread得到的都是空指针。
这一点看auto_ptr拷贝构造函数的源码就清楚了
auto_ptr(auto_ptr_ref<_Ty> _Right) _THROW0()
{ // construct by assuming pointer from _Right auto_ptr_ref
_Ty *_Ptr = _Right._Ref;
_Right._Ref = 0;// release old
_Myptr = _Ptr;// reset this
}
release old明确将原来的指针引用设置为了0。
再来一个问题。auto_ptr可以用于stl容器吗?
引用Effective STL
第八条 永不建立auto_ptr的容器 说不行。
的确不行 因为这样的代码根本没法编译过去 (改成shared_ptr是可以编译的,后续讲shared_ptr再说)
std::list
C++委员会不允许编译通过的原因是对容器里的数据进行操作很多情况下会发生所有权的转移,例如 在对容器元素进行比较的时候。
所以这样的代码是不行的。
永远记住 std::auto_ptr 所有权会发生转移。当然用这个智能指针的的很少,还是需要了解一下。
第二种 std::shared_ptr
shared_ptr 就是为了解决,所有权转移出现的问题。连名字都叫shared。说明这个ptr是可以共享的。
也就是
std::shared_ptr
std::shared_ptr
ptr2=ptr;
ptr2->Show();
ptr->Show();
其中
ptr2=ptr;赋值的时候 所有权不发生转移。而是共享所有权。所以ptr2和ptr都指向同一个对象。
那怎么确定什么时候这个对象要被delete呢?
shared_ptr内部用一个计数器来确定对象被引用的次数。
引用的时候计数器增加一,当然这个引用计数也是在堆上分配的。确保指向同一个对象的引用计数是同一块内存。取消引用的时候计数器减1
计数器减到0的时候就删除这个对象。
所以std::shared_ptr
ptr2=ptr;
该计数器变成2
离开这个函数时先析构ptr2 计数器-1 变成1
再析构ptr 计数器-1 变成0.已经没有指针引用这个对象了。所以delete sample。
所以shared_ptr可以用来放在容器当中,因为不会引发所有权转移的问题。