深入学习c++--智能指针(二) weak_ptr(打破shared_ptr循环引用)

1. 几种智能指针

1. auto_ptr: c++11中推荐不使用他(放弃)

2. shared_ptr: 每添加一次引用 就+1,减少一次引用,就-1;做到指针进行共享

3. unique_ptr: 一个指针同时只能有一个使用者使用

4. weaked_ptr: 与shared_ptr搭配使用

1.1 weak_ptr

参考:https://zh.cppreference.com/w/cpp/memory/weak_ptr

  • std::weak_ptr 是一种智能指针,它对被 std::shared_ptr 管理的对象存在 非拥有性(“弱”)引用。在访问所引用的对象前必须 先转换为 std::shared_ptr
  • std::weak_ptr 用来 表达临时所有权的概念
    • 当某个对象只有存在时才需要被访问,而且随时可能被他人删除时,可以使用 std::weak_ptr 来跟踪该对象。
    • 需要获得临时所有权时,则将其转换为 std::shared_ptr,此时如果原来的 std::shared_ptr被销毁,则该对象的生命期将被延长至这个临时的 std::shared_ptr 同样被销毁为止。
  • std::weak_ptr 的另一用法是:打断 std::shared_ptr 所管理的对象组成的环状引用。(打破shared_ptr的循环引用)
    • 若这种环被孤立(例如无指向环中的外部共享指针),则 shared_ptr 引用计数无法抵达零,而内存被泄露。
    • 能令环中的指针之一为弱指针以避免此情况。

循环引用的问题:该被调用的析构函数没有被调用

#include <iostream>
#include <memory>
using namespace std;

class Parent;
typedef std::shared_ptr<Parent> ParentPtr;

class Child
{
public:
    ParentPtr father;
    Child() {
        cout << "hello Child" << endl;
    }
    ~Child() {
        cout << "bye Child\n";
    }
};

typedef std::shared_ptr<Child> ChildPtr;

class Parent {
public:
    ChildPtr son;
    Parent() {
        cout << "hello parent\n";
    }
    ~Parent() {
        cout << "bye Parent\n";
    }
};

void testParentAndChild()
{
    ParentPtr p(new Parent());
    ChildPtr c(new Child());
    p->son = c;
    c->father = p;
}

int main()
{
    testParentAndChild();
    return 0;
}

问题:c只有调用p的析构的时候,才能被释放。p只有调用c的析构的时候,才能被释放。。形成了循环引用,造成了内存泄露

因此,引入新的智能指针,weak_ptr

1.2 weak_ptr基本用法

一旦外部指向shared_ptr资源失效,那么weak_ptr管理的资源自动失效

#include <iostream>
#include <cassert>
#include <memory>
using namespace std;

class Object
{
public:
    Object(int id) : m_id(id) {
        std::cout << "init obj " << m_id << std::endl;
    }
    ~Object() {
        std::cout << "bye bye " << m_id << std::endl;
    }
    int id() const {
        return m_id;
    }
private:
    int m_id;
};

void sharedPtrWithWeakPtr()
{
    typedef std::shared_ptr<Object> ObjectPtr;
    typedef weak_ptr<Object> WeakObjectPtr;

    ObjectPtr obj(new Object(1));

    // 一旦外部指向shared_ptr资源失效,那么weak_ptr管理的资源自动失效 

    WeakObjectPtr weakObj2;           // 裸指针
    WeakObjectPtr weakObj(obj);       // 指向shared_ptr指针
    WeakObjectPtr weakObj3(obj);
    cout << "obj use count is " << obj.use_count() << endl;   // 1--尽管weak_ptr也指向obj,但他只是监听者,本身并不影响引用计数次数
    {
        // weak_ptr使用方法
        // 外部至少还有一个shared_ptr来管理资源,同时p自己本身的资源 -- p.use_count >= 2
        auto p = weakObj.lock();         // auto == ObjectPtr
        if (p) {
            cout << p.unique() << endl;  // 0 -- p.use_count() >= 2
        }
        else {
        }
    }
    // 不指向某份资源
//    obj.reset();
//    {
//        auto p = weakObj.lock();      // auto == ObjectPtr
//        if (p) {
//            assert(false);
//        }
//        else {
//            cout << "null" << endl;   // 如果调用reset,就会执行这句话
//        }
//    } 

    // 此时, Object(1)已经失效
    obj.reset(new Object(2)) ;
    {
        auto p = weakObj.lock();       // 返回值如果有效, 在外部必须有weakObj指向同一个资源
        if (p) {
            assert(false);
        }
        else {
            cout << "null " << endl;   // null
        }
    }

    weakObj = obj;    // 又有效了
    // 想知道weak_ptr有没有管理一份资源(外部有没有资源), 又不想生成一个shared_ptr
    if (weakObj.expired()) {
        // 资源过期了 -- true
        cout << "no ptr" << endl;
    }
    else {
        cout << "have resource\n";
    }
}

int main()
{

    sharedPtrWithWeakPtr();
    return 0;

}

1.3 解决类之间循环引用

一旦外部指向shared_ptr资源失效,那么weak_ptr管理的资源自动失效

#include <iostream>
#include <cassert>
#include <memory>
using namespace std;

class Parent;
typedef std::shared_ptr<Parent> ParentPtr;
typedef std::weak_ptr<Parent> WeakParentPtr;

class Child
{
public:
    WeakParentPtr father;                 // 只有一环换成 weak_ptr, 即可打破环
    Child() {
        cout << "hello Child" << endl;
    }
    ~Child() {
        cout << "bye Child\n";
    }
};

typedef std::shared_ptr<Child> ChildPtr;
typedef std::weak_ptr<Child> WeakChildPtr;

class Parent {
public:
    ChildPtr son;
    Parent() {
        cout << "hello parent\n";
    }
    ~Parent() {
        cout << "bye Parent\n";
    }
};

void testParentAndChild()
{
    ParentPtr p(new Parent());
    ChildPtr c(new Child());
    p->son = c;
    c->father = p;
    cout << (c->father).use_count() << endl;
    cout << (p->son).use_count() << endl;
}

int main()
{
    testParentAndChild();
    return 0;
}

析构函数成功调用, 注意析构顺序,谁是weak_ptr对象,就先析构谁。

1.4 用 enable_shared_from_this从this转换到shared_ptr

  • 类中函数接口需要一个本对象智能指针的const引用 (如何生成本对象的智能指针)
  • 用 enable_shared_from_this从this转换到shared_ptr  (使用CRTP来使用 本对象的智能指针)

主要代码:

handleChildAndParent(shared_from_this(), ps);

完整代码:

#include <iostream>
#include <cassert>
#include <memory>
using namespace std;

class Parent;
typedef std::shared_ptr<Parent> ParentPtr;
typedef std::weak_ptr<Parent> WeakParentPtr;

class Child : public std::enable_shared_from_this<Child>  // 奇异模板参数 CRTP
{
public:
    WeakParentPtr father;                 // 只有一环换成 weak_ptr, 即可打破环
    Child() {
        cout << "hello Child" << endl;
    }
    ~Child() {
        cout << "bye Child\n";
    }
    void CheckRelation() {

    }
};

typedef std::shared_ptr<Child> ChildPtr;
typedef std::weak_ptr<Child> WeakChildPtr;

void handleChildAndParent(const ParentPtr& p, const ChildPtr& c);

class Parent : public enable_shared_from_this<Parent> {
public:
    WeakChildPtr son;
    Parent() {
        cout << "hello parent\n";
    }
    ~Parent() {
        cout << "bye Parent\n";
    }
    void CheckRelation() {
        auto ps = son.lock();
        if (ps) {
            //原理: 类中存在weak_ptr指针,通过一系列方式转换成 shared_ptr,然后传参
            handleChildAndParent(shared_from_this(), ps);    // 使用CRTP来使用 本对象的指针 ★ ★ ★ ★
        }
        cout << "after call checkRelation\n";
    }
};

void testParentAndChild()
{
    ParentPtr p(new Parent());
    ChildPtr c(new Child());
    p->son = c;
    c->father = p;
    cout << (c->father).use_count() << endl;
    cout << (p->son).use_count() << endl;

    p->CheckRelation();
}

void handleChildAndParentRef(const Parent& p, const Child& c)
{
    auto cp = c.father.lock();
    auto pc = p.son.lock();
    if (cp.get() == &p && pc.get() == &c)
    {
        cout << "right relation" << endl;
    }
    else {
        cout << "oop !!!\n";
    }
}

// const xxx&: 减少拷贝次数
void handleChildAndParent(const ParentPtr& p, const ChildPtr& c)
{
    auto cp = c->father.lock();
    auto pc = p->son.lock();
    if (cp == p && pc == c)
    {
        cout << "right relation\n";
    }
    else {
        cout << "oop!!!\n";
    }
}

int main()
{
    testParentAndChild();
    return 0;
}

 

原文地址:https://www.cnblogs.com/douzujun/p/10803365.html

时间: 2024-08-02 11:05:14

深入学习c++--智能指针(二) weak_ptr(打破shared_ptr循环引用)的相关文章

智能指针的模拟实现shared_ptr 循环引用 定置删除器

auto_ptr与scoped_ptr的实现见本人的上篇博客. 三.shared_ptr shared_ptr的实现原理是通过引用计数来实现,只有当引用计数为1时才释放空间,否则只需将引用计数减1.拷贝和赋值将引用计数加1,具体代码如下: template <typename T> class SharedPtr { public: SharedPtr(); SharedPtr(T* ptr); SharedPtr(const SharedPtr<T>& ap); ~Sha

C++细节学习之智能指针auto_ptr和tr1::shared_ptr

为什么要用智能指针? 我们知道在C++中,资源管理是个头疼的问题.资源管理最常做的就是内存管理.而指针用起来如果忘记归还,就会导致内存泄露. 比如: class Oneclass{}; int func(){ Oneclass* one=new Oneclass; if(!one)return 1; delete one; return 0; } 可见,上面代码中,我们可以发现delete并没什么卵用,函数没有执行到delete,而是直接返回了1:最好的办法就是不用指针,使用对象来管理资源,因为

【STL学习】智能指针之shared_ptr

前面已经学习过auto_ptr,这里补充另外一种智能指针,比auto_ptr要更强力更通用的shared_ptr. shared_ptr 简介及使用选择  几乎所有的程序都需要某种形式的引用计数智能指针,这种指针让我们不再需要为两个对象或更多对象共享的对象的生命周期而编写复杂的逻辑(写起来有点绕口),当被共享的对象引用计数降为0时,被共享对象被自动析构. 引用计数指针分为插入式(instrusive)和非插入式(non-instrusive)两种.前者要求它所管理的类提供明确的函数或数据成员用于

c++ 11学习笔记--智能指针

C++ 98的 std::auto_ptr已经被彻底遗弃了,取而代之的是unique_ptr.shared_ptr与weak_ptr.大部分时候我们自己手动申请内存方式内存都是没有问题的,问题是如果程序很大了之后,一个复杂的对象,多次拷贝的代价非常高,很多地方都会使用到,只存在一份拷贝显然是最好的,这个时候对象生命周期的管理就会很复杂,所以c++引入了智能指针. 任何事物都会有两面性. Shared_ptr 摘录于Effective C++, 3rd Edition, Item 17: 在 st

解释清楚智能指针二【用自己的话,解释清楚】

写在前面 用自己的话分析清楚~ 智能指针是如何使用的? 强指针是如何实现? 弱指针如何转化为强指针? 智能指针的使用 智能指针的使用必须满足如下条件: 这个类需要继承自RefBase 为什么需要虚析构函数? 虚析构函数是为了解决这样的一个问题:基类的指针指向派生类对象,并用基类的指针删除派生类对象.虚函数的出现是为了解决多态问题. 满足上述条件的类就可以定义智能指针了,普通的指针使用如下方式: MyClass *p_obj; 智能指针是这样定义: Sp<MyClass> p_obj; 强指针的

C++2.0新特性(七)——&lt;Smart Pointer(智能指针)之weak_ptr&gt;

一.weak_ptr出现的意义 上一节提到过shared_ptr,它会自动释放“不再需要使用的对象”的相应的资源,但是它不是万能的,在某些时候(比如说循环引用),它会显得力不从心,这就是weak_ptr出现的意义: 1.1 weak_ptr 使用特性 weak_ptr也是一个模板,只提供能接受一个shared_ptr的构造函数或者另一个weak_ptr的赋值,也就是说不能直接用它定义一个智能指针对象,它是为了搭配shared_ptr使用的,weak_ptr提供lock.swap.reset.ex

深入学习c++--智能指针

1. 几种智能指针 1. auto_ptr: c++11中推荐不使用他 2. shared_ptr: 每添加一次引用 就+1,减少一次引用,就-1:做到指针进行共享 3. unique_ptr: 一个指针同时只能有一个使用者使用 4. weaked_ptr: 与shared_ptr搭配使用 1.1 shared_ptr 参考:https://zh.cppreference.com/w/cpp/memory/shared_ptr std::shared_ptr 是通过指针保持对象共享所有权的智能指

[转] weak_ptr解决shared_ptr环状引用所引起的内存泄漏

http://blog.csdn.net/liuzhi1218/article/details/6993135 循环引用: 引用计数是一种便利的内存管理机制,但它有一个很大的缺点,那就是不能管理循环引用的对象.一个简单的例子如下: #include<string> #include <iostream> #include <boost/shared_ptr.hpp> #include <boost/weak_ptr.hpp> class parent; cl

c++学习笔记——智能指针

智能指针是为了便于管理动态内存,能够自动管理释放所指向的对象. 智能指针共有三种:1.shared_ptr允许多个指针指向同一个对象:2.unique_ptr独占所指向的对象:3.weak_ptr是一个伴随类,它是一种弱引用,指向shared_ptr所管理的对象.这三种类型都定义在memory头文件中. 下面是一个在网上看到的智能指针的实现原理代码: class U_Ptr { friend class HasPtr; int *ip; size_t use; U_Ptr(int *p) : i