智能指针的原理:智能指针是一个类,这个类的构造函数中传入一个普通指针,析构函数中释放传入的指针指向的内存。智能指针本身是栈上的对象,所以当函数(程序)结束时会被自动释放掉。
1) auto_ptr:主要是为了解决“有异常抛出时发生内存泄漏”的问题 。因为发生异常而无法正常释放内存。不支持复制和赋值,但复制和赋值时不提示错误,不能放入容器中。
2)unique_ptr:独享指针,不支持复制和赋值,但是赋值会编译出错。提供move方法。
3)shared_ptr:共享指针,基于引用计数的智能指。可以随意地赋值。
对被管理的资源进行引用计数,当一个shared_ptr对象要共享这个资源的时候,该资源的引用计数加1,当这个对象生命期结束的时候,再把该引用计数减少1。这样当最后一个引用它的对象被释放的时候,资源的引用计数减少到0,此时释放该资源。缺点是使用不当,可能存在“环状引用”引起内存泄露。
4) weak_ptr:weak_ptr是为了配合shared_ptr而引入的一种智能指针,它指向一个由shared_ptr管理的对象而不影响所指对象的生命周期,也就是将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。不论是否有weak_ptr指向,一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。从这个角度看,weak_ptr更像是shared_ptr的一个助手而不是智能指针。
C++ 11 模板库的 memory头文件中定义了智能指针,即 shared _ptr 模板;
只要将 new 运算符返回的指针 p 交给一个 shared_ptr 对象“托管”,就不必担心在哪里写delete p语句——实际上根本不需要编写这条语句,托管 p 的 shared_ptr 对象在消亡时会自动执行delete p。而且,该 shared_ptr 对象能像指针 p —样使用,即假设托管 p 的 shared_ptr 对象叫作 ptr,那么 *ptr 就是 p 指向的对象。
通过 shared_ptr 的构造函数,可以让 shared_ptr 对象托管一个 new 运算符返回的指针,写法如下:
shared_ptr ptr(new T); // T 可以是 int、char、类等各种类型
此后,ptr 就可以像 T* 类型的指针一样使用,即 *ptr 就是用 new 动态分配的那个对象。
多个 shared_ptr 对象可以共同托管一个指针 p,当所有曾经托管 p 的 shared_ptr 对象都解除了对其的托管时,就会执行delete p。
比较典型的情况是循环引用问题,比如这段代码
class B; // 前置声明
class A {
public:
shared_ptr<B> ptr;
};
class B {
public:
shared_ptr<A> ptr;
};
int main()
{
while(true) {
shared_ptr<A> pa(new A()); //shared_ptr pa = make_shared();
shared_ptr<B> pb(new B()); //shared_ptr pa = make_shared();
pa -> ptr = pb;
pb -> ptr = pa;
}
return 0;
}
这个程序中智能指针的引用情况如下图:
上图中,class A和class B的对象各自被两个智能指针管理,也就是A object和B object引用计数都为2,为什么是2?
分析class A对象的引用情况,该对象被main函数中的pa和class B对象中的ptr管理,因此A object引用计数是2,B object同理。
在这种情况下,在main函数中一个while循环结束的时候,pa和pb的析构函数被调用,但是class A对象和class B对象仍然被一个智能指针管理,A object和B object引用计数变成1,于是这两个对象的内存无法被释放,造成内存泄漏,如下图所示:
解决方法:
解决方法很简单,把class A或者class B中的shared_ptr改成weak_ptr即可,由于weak_ptr不会增加shared_ptr的引用计数,所以A object和B object中有一个的引用计数为1,在pa和pb析构时,会正确地释放掉内存.
class B; // 前置声明
class A {
public:
weak_ptr<B> ptr;
};
class B {
public:
shared_ptr<A> ptr;
};
int main()
{
while(true) {
shared_ptr<A> pa(new A()); //shared_ptr pa = make_shared();
shared_ptr<B> pb(new B()); //shared_ptr pa = make_shared();
pa -> ptr = pb;
pb -> ptr = pa;
}
return 0;
}