C++智能指针及循环引用

目录:

  • auto_ptr
  • unique_ptr
  • shared_ptr
  • weak_ptr
  • 循环引用问题

智能指针的原理:

智能指针的原理:智能指针是一个类,这个类的构造函数中传入一个普通指针,析构函数中释放传入的指针指向的内存。智能指针本身是栈上的对象,所以当函数(程序)结束时会被自动释放掉。
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的一个助手而不是智能指针。

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;
}

这个程序中智能指针的引用情况如下图:
C++智能指针及循环引用_第1张图片
上图中,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,于是这两个对象的内存无法被释放,造成内存泄漏,如下图所示:
C++智能指针及循环引用_第2张图片
解决方法:
解决方法很简单,把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;
}

你可能感兴趣的:(cpp,c++,指针)