C++11智能指针之weak_ptr详解

yizhihongxing

C++11智能指针之weak_ptr详解

简介

C++11添加了4种智能指针:unique_ptr、shared_ptr、weak_ptr、auto_ptr。其中weak_ptr是一种弱引用类型的指针,它不对所指对象进行引用计数,可以防止 shared_ptr 的循环引用问题。

特点

  • weak_ptr 所指向的对象可能已经被删除了,因此在使用 weak_ptr 之前需要进行判空操作;
  • weak_ptr 不能直接访问其所指向的对象,需要将其转化为 shared_ptr 才能进行访问;
  • 不要将一个 new 出来的对象直接赋值给 weak_ptr,这样会造成强制删除动态分配对象,从而导致行为未定义。

使用场景

  1. 防止 shared_ptr 循环引用问题

当对象之间存在循环引用的时候,shared_ptr 会在引用计数清零时,删除对象,但是循环引用导致引用计数无法减为零,致使引起内存泄露。此时可以用 weak_ptr 来解决这个问题。

例如:

struct Node {
    std::shared_ptr<Node> next;
};

std::shared_ptr<Node> n1(new Node);
std::shared_ptr<Node> n2(new Node);
n1->next = n2; // n1 引用计数为 1,n2 引用计数为 2
n2->next = n1; // n1 引用计数为 2,n2 引用计数为 2

上面的例子会导致循环引用,引用计数无法清零,使用 weak_ptr 可以解决这个问题。

struct Node {
    std::weak_ptr<Node> next; // 使用 weak_ptr 替代 shared_ptr
};

std::shared_ptr<Node> n1(new Node);
std::shared_ptr<Node> n2(new Node);
n1->next = n2;
n2->next = n1;
  1. 用于缓存对象

当对象不再被引用时,可以选择删除它从而释放资源。然而,在某些情况下,被引用的对象可能在之后还需要用到。此时,可以将这些对象保存在缓存中。缓存不会持有对象的所有权,而只是提供了对象的一种访问方式。使用 weak_ptr 可以实现这个功能。

例如:

class Cache {
public:
    void add(const std::string& name, std::shared_ptr<Object> obj) {
        cache[name] = obj;
    }

    std::weak_ptr<Object> get(const std::string& name) {
        auto iter = cache.find(name);
        if (iter != cache.end()) {
            return iter->second;
        }
        return std::weak_ptr<Object>();
    }

private:
    std::map<std::string, std::shared_ptr<Object>> cache;
};

在上面的例子中,Cache 对象只会保存 Object 对象的弱引用,这样就不会阻止 Object 对象的销毁,同时可以在需要时访问 Object 对象。

示例

示例一:循环引用时使用 weak_ptr

#include <memory>
#include <iostream>

class B;

class A {
public:
    std::weak_ptr<B> pb_;
    ~A() {
        std::cout << "A is destroyed." << std::endl;
    }
};

class B {
public:
    std::shared_ptr<A> pa_;
    ~B() {
        std::cout << "B is destroyed." << std::endl;
    }
};

int main() {
    std::shared_ptr<A> a(new A);
    std::shared_ptr<B> b(new B);
    a->pb_ = b;
    b->pa_ = a;

    return 0;
}

上面的代码存在循环引用,导致 a 和 b 无法被释放。修改如下:

class B;

class A {
public:
    std::weak_ptr<B> pb_;
    ~A() {
        std::cout << "A is destroyed." << std::endl;
    }
};

class B {
public:
    std::weak_ptr<A> pa_;
    ~B() {
        std::cout << "B is destroyed." << std::endl;
    }
};

int main() {
    std::shared_ptr<A> a(new A);
    std::shared_ptr<B> b(new B);
    a->pb_ = std::weak_ptr<B>(b);
    b->pa_ = std::weak_ptr<A>(a);

    return 0;
}

将 A 类中的 pb_ 指针改为 weak_ptr 类型,B 类中的 pa_ 指针也改为 weak_ptr 类型,就可以正确释放对象了。

示例二:用于缓存对象

#include <iostream>
#include <memory>
#include <string>
#include <map>

class Object {
public:
    Object(const std::string& name) : name_(name) {}
    ~Object() {
        std::cout << name_ << " is destroyed." << std::endl;
    }
private:
    std::string name_;
};

class Cache {
public:
    void add(const std::string& name, std::shared_ptr<Object> obj) {
        cache[name] = obj;
    }

    std::weak_ptr<Object> get(const std::string& name) {
        auto iter = cache.find(name);
        if (iter != cache.end()) {
            return iter->second;
        }
        return std::weak_ptr<Object>();
    }

private:
    std::map<std::string, std::shared_ptr<Object>> cache;
};

int main() {
    std::shared_ptr<Object> o1 = std::make_shared<Object>("object1");
    std::shared_ptr<Object> o2 = std::make_shared<Object>("object2");

    Cache cache;
    cache.add("obj1", o1);
    std::weak_ptr<Object> wo1 = cache.get("obj1");
    if (!wo1.expired()) {
        std::shared_ptr<Object> so1 = wo1.lock();
        std::cout << "Name: " << "obj1" << ", Object: " << so1 << std::endl;
    }

    cache.add("obj2", o2);
    std::weak_ptr<Object> wo2 = cache.get("obj2");
    if (!wo2.expired()) {
        std::shared_ptr<Object> so2 = wo2.lock();
        std::cout << "Name: " << "obj2" << ", Object: " << so2 << std::endl;
    }

    std::weak_ptr<Object> wo3 = cache.get("obj3");
    if (wo3.expired()) {
        std::cout << "Object is expired." << std::endl;
    }

    return 0;
}

在上面的代码中,Cache 对象只会保存 Object 对象的弱引用,这样就不会阻止 Object 对象的销毁,同时可以在需要时访问 Object 对象。这样就可以做到缓存而不造成内存泄漏。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++11智能指针之weak_ptr详解 - Python技术站

(0)
上一篇 2023年5月22日
下一篇 2023年5月22日

相关文章

  • JavaScript中的JSON转为Python可读取

    JSON是一种轻量级的数据交换格式,Python是一门强大的编程语言,两者都是在不同领域的应用,通常在Web开发中,我们会使用JavaScript解析JSON数据,但有时候需要将JSON数据转为Python可读取的形式。以下是一些方法: 使用Python内置库json模块 在Python中,我们可以使用内置库json模块来解析JSON数据。步骤如下: 导入j…

    C 2023年5月23日
    00
  • C++begin和end运算符的返回迭代器的类型如何判断?

    C++中,begin()和end()函数是STL容器中的常见函数,它们返回一个迭代器,分别指向容器的第一个元素和最后一个元素的下一位,常用于遍历和操作容器中的元素。下面开始讲解如何判断begin()和end()运算符的返回类型。 1. 查看容器的迭代器类型 begin()和end()是根据容器类型来决定返回的迭代器类型的。因此,我们首先要查看对应的容器的迭代…

    C 2023年5月23日
    00
  • C++简单QQ程序服务器端的实现代码

    下面我将为你详细讲解如何实现“C++简单QQ程序服务器端的实现代码”。 一、需求分析 在开始编写程序之前,我们需要先进行需求分析,明确程序的功能和实现方式。根据题目描述,我们需要实现一个C++简单QQ程序服务器端的实现代码,其主要功能包括: 用户可以注册账号并登录; 用户可以添加好友,删除好友,查询好友列表; 用户可以发送消息给好友; 用户可以查看收到的消息…

    C 2023年5月23日
    00
  • 谷歌Pixel C平板怎么样?与微软Win10平板Surface 3对比详解

    谷歌Pixel C平板怎么样?与微软Win10平板Surface 3对比详解 引言 谷歌于2015年底发布了Pixel C平板,作为谷歌自家产品线上的一款旗舰平板,它与微软Win10平板Surface 3都是市面上备受关注的产品。在本文中,我们将对Pixel C平板与Surface 3进行详细对比,并从硬件、软件两个方面进行分析。 硬件部分 设计 Pixel…

    C 2023年5月23日
    00
  • 水牛(shuiniu.exe)手工查杀方法不用专杀工具

    首先,需要明确的是,“水牛(shuiniu.exe)”是一种特殊的病毒文件,可以通过手工操作来进行查杀。 以下是手工查杀“水牛(shuiniu.exe)”病毒的详细攻略: 1. 确认病毒文件路径 在开始手工查杀之前,首先需要确认病毒文件的路径。可以通过查看任务管理器或检查磁盘根目录下是否存在“shuiniu.exe”文件来确定。 2. 关闭病毒进程 如果已经…

    C 2023年5月22日
    00
  • c++读写文件流实例程序讲解

    C++读写文件流实例程序讲解 1. 概述 C++中,通过文件流(fstream)可以方便地进行文件读写操作。文件流不仅可以读写文本文件,还可以读写二进制文件。对于程序开发中需要处理的大量数据,文件读写操作显得尤为重要。 本文将详细讲解如何在C++中使用文件流进行文件读写操作。 2. 文件流的基本操作 C++文件流中,主要有三种文件流类型:ifstream、o…

    C 2023年5月23日
    00
  • C++面向对象实现万年历的示例代码

    以下是对 C++面向对象实现万年历的示例代码 的详细讲解攻略。 前置知识 在学习本教程前,我们需要掌握以下基础知识: C++的基本语法 C++中的面向对象编程 C++中文件操作的基本操作 C++中的日期和时间处理 示例代码 下面是一个简单的C++面向对象实现万年历的示例代码: #include <iostream> #include <fs…

    C 2023年5月22日
    00
  • C语言算法金手指摩尔投票法手撕绝大多数问题

    C语言算法金手指——摩尔投票法 什么是摩尔投票法 摩尔投票法是一种用于在数组中查找最多元素的算法,其基本思想是采用抵消的方式,即将数组中的某个元素和其他元素抵消,如果最后剩下的元素个数大于数组长度的一半,则该元素即为所求。 摩尔投票法的过程 假设我们要查找一个数组 nums 中的最多元素,我们可以通过如下流程来实现: 初始化数字x和计数器count,将它们都…

    C 2023年5月22日
    00
合作推广
合作推广
分享本页
返回顶部