c++11中引入了之前在boost库中的智能指针,智能指针有三种shared_ptr, weak_ptr, unique_ptr。
引入智能指针的作用是为了保证在出现异常时,也能保证堆内存会被释放掉,如下面代码:
void fun() { T *t = new T(); //do something delete t; return; }
在do something的时候,如果出现了异常或者其他原因,导致提前结束离开函数,那么new出来的内存将不会被释放。而使用智能指针管理这块内存时,会通常会在智能指针本身被析构时,正确的释放这个new出来的内存。
其中shared_ptr用于共享某一块内存,shared_ptr维护了一个足够大的引用计数,保证在引用计数归0时正确的释放掉堆中的内存,但是仅仅使用shared_ptr是不够的,如下面的代码:
class B; class A { public: shared_ptr<B> p; }; class B { public: shared_ptr<A> p; }; int main() { while (true) { shared_ptr<A> pa(new A()); shared_ptr<B> pb(new B()); pa->p = pb; pb->p = pa; } return 0; }
在while循环中,先是在栈中构造了两个智能指针,分别管理两块堆内存,记为A, B。然后两个赋值语句,使得在shared_ptr中,A,B的引用计数均为2,所以在析构掉pa与pb时,他们的引用计数都没能到达0,于是发生了循环引用,于是开始内存泄露,用不了多久内存爆炸程序死掉,运气不好还会死机> , <
解决方案也很简单,将类A,B中的一个shared_prt改为weak_ptr即可,weak_ptr不会增加shared_ptr的引用计数,所以在pa,pb中会有一个的引用计数为1,在它析构时,会正确的释放掉内存。
所以shared_ptr是用来共享内存,而weak_ptr是用来避免循环引用的。 使用weak_ptr时需要用lock检查weak_ptr保存的指针是否有效。
此外unique_ptr是一种特殊的shared_ptr,它的拷贝构造函数以及拷贝运算符是删除的,这意味着当我们这样初始化一个unique_ptr时,它会独占内存:
unique_ptr up(new T());
我们丢失了原生指针,而且unique_ptr不能复制,当然如果非得作死这么写也是不会报错的:
T *p = new T();
unique_ptr up1(p);
unique_ptr up2(p);
这样两个unique_ptr有了同样的原生指针- -!但是这么写毫无意义。
值得注意的是unique_ptr虽然不支持拷贝,但是是支持移动的,所以它通常这么用:
unique_ptr<int> fun() { unique_ptr<int> up(new int(999)); return up; } int main() { unique_ptr<int> up = fun(); return 0; }
保证指针唯一的情况下,还能传递内存的所有权。