文档章节

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

o
 osc_byhxg8pe
发布于 2019/05/02 20:58
字数 1315
阅读 79
收藏 0
c++

「深度学习福利」大神带你进阶工程师,立即查看>>>

1. 几种智能指针

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

2. shared_ptr: 拥有共享对象所有权语义的智能指针 

3. unique_ptr: 拥有独有对象所有权语义的智能指针 

4. weaked_ptr: 到 std::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;
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

o
粉丝 0
博文 500
码字总数 0
作品 0
私信 提问
加载中
请先登录后再评论。
Netty那点事(三)Channel与Pipeline

Channel是理解和使用Netty的核心。Channel的涉及内容较多,这里我使用由浅入深的介绍方法。在这篇文章中,我们主要介绍Channel部分中Pipeline实现机制。为了避免枯燥,借用一下《盗梦空间》的...

黄亿华
2013/11/24
2W
22
Flappy Bird(安卓版)逆向分析(一)

更改每过一关的增长分数 反编译的步骤就不介绍了,我们直接来看反编译得到的文件夹 方法1:在smali目录下,我们看到org/andengine/,可以知晓游戏是由andengine引擎开发的。打开/res/raw/at...

enimey
2014/03/04
6K
18
【opencv】图形的绘制

1.矩形图像的绘制: 原函数:void cvRectangle(CvArr* img, CvPoint pt1, CvPoint pt2, CvScalar color, int thickness=1, int line_type=8,int shift=0) img就是需要绘制的图像 pt1 and pt......

其实我是兔子
2014/10/08
1.2K
1
Swift百万线程攻破单例(Singleton)模式

一、不安全的单例实现 在上一篇文章我们给出了单例的设计模式,直接给出了线程安全的实现方法。单例的实现有多种方法,如下面: class SwiftSingleton { } 这段代码的实现,在shared中进行条...

一叶博客
2014/06/20
3.4K
16
Nutch学习笔记4-Nutch 1.7 的 索引篇 ElasticSearch

上一篇讲解了爬取和分析的流程,很重要的收获就是: 解析过程中,会根据页面的ContentType获得一系列的注册解析器, 依次调用每个解析器,当其中一个解析成功后就返回,否则继续执行下一个解...

强子哥哥
2014/06/26
712
0

没有更多内容

加载失败,请刷新页面

加载更多

Python设计模式(12):命令模式

在实际生活中,社会分工十分明确。一个常见的实例是餐厅的就餐者、服务员和厨师之间的关系。就餐者向服务员提供一些具体的要求,例如告诉服务员“我们有 6 个人,要吃海鲜,请按照每人 60 元...

不可言诉的深渊
2019/03/31
0
0
树状数组

前言 如果你在考提高组的前一天还对这有疑问,那你会与一等奖失之交臂; 如果你还在冲击普及组一等奖,那这篇博客会浪费你人生中宝贵的5~20分钟。 (这句话摘自Dijkstra_Liu的blog ) 概念 ...

osc_qgfjs4a5
49分钟前
0
0
Codeforces Round 662 赛后解题报告

Codeforces Round 662 赛后解题报告 梦幻开局到1400+的悲惨故事 A. Rainbow Dash, Fluttershy and Chess Coloring 这个题很简单,我们可以画几张图,发现每一次我们染色的最佳方法就是每次往...

osc_i5oyb1xr
50分钟前
0
0
商城直播系统这么火爆,这些优势及好处你了解吗?

随着互联网的发展,电商已经占领我们生活的大部分,越来越多的人开始做电商这个行业,商城系统已经不是什么稀奇事了,今年直播商城系统又开启的电商的新篇章,直播商城系统为什么突然这么火爆...

涛涛很酷
50分钟前
17
0
树状数组

前言 如果你在考提高组的前一天还对这有疑问,那你会与一等奖失之交臂; 如果你还在冲击普及组一等奖,那这篇博客会浪费你人生中宝贵的5~20分钟。 (这句话摘自Dijkstra_Liu的blog ) 概念 ...

osc_gxvh47u5
51分钟前
13
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部