C++笔记 - std::weak_ptr

593 阅读1分钟

在C++中std::shared_ptr可以实现多个对象共享同一块内存,但存在循环引用的问题。即两个shared_ptr互相指向对方,导致引用计数无法被递减到0,造成内存泄露。下面看一个有问题的例子:

#include <iostream>
#include <memory>

class Any {
  public:
    friend bool set_relationship(std::shared_ptr<Any> & p1,
                                 std::shared_ptr<Any> & p2) {
        if (!p1 || !p2) {
            return false;
        }

        p1->m_another = p2;
        p2->m_another = p1;
        return true;
    }

  private:
    std::shared_ptr<Any> m_another;
};

int main(void) {
    auto p1 = std::make_shared<Any>();
    auto p2 = std::make_shared<Any>();
    set_relationship(p1, p2);
    return 0;
}

通过valgrind 工具进行内存检查:

==1996== LEAK SUMMARY:
==1996==    definitely lost: 32 bytes in 1 blocks
==1996==    indirectly lost: 32 bytes in 1 blocks
==1996==      possibly lost: 0 bytes in 0 blocks
==1996==    still reachable: 0 bytes in 0 blocks
==1996==         suppressed: 0 bytes in 0 blocks

使用shared_ptr导致循环引用,的确造成了内存泄露。


下面看一个使用weak_ptr的例子:

#include <iostream>
#include <memory>

class Any {
  public:
    Any(int a) : m_a(a) {}

    friend bool set_relationship(std::shared_ptr<Any> &p1,
                                 std::shared_ptr<Any> &p2) {
        if (!p1 || !p2) {
            return false;
        }

        // weak_ptr重载的赋值运算符中可以接收shared_ptr对象
        p1->m_another = p2;
        p2->m_another = p1;
        return true;
    }

    void show(void) { std::cout << m_a << std::endl; }

    std::weak_ptr<Any> &get_ptr(void) { return m_another; }

  private:
    int m_a;
    std::weak_ptr<Any> m_another;
};

int main(void) {
    auto p1 = std::make_shared<Any>(1);
    auto p2 = std::make_shared<Any>(2);
    set_relationship(p1, p2);
    
    assert(p1.use_count() == 1);
    assert(p2.use_count() == 1);

    p1->get_ptr().lock()->show(); // 输出:2
    return 0;
}

通过valgrind 工具进行内存检查:

==4775== HEAP SUMMARY:
==4775==     in use at exit: 0 bytes in 0 blocks
==4775==   total heap usage: 3 allocs, 3 frees, 72,768 bytes allocated
==4775==
==4775== All heap blocks were freed -- no leaks are possible

通过weak_ptr 能够解决shared_ptr循环引用带来的内存泄露问题。