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

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日

相关文章

  • C语言实现数独程序的示例代码

    下面是关于“C语言实现数独程序的示例代码”的完整攻略: 一、编写数独程序的流程 1. 确定程序输入和输出 数独程序的输入应该是一个9×9的矩阵,即数独的谜题,其中0表示未知格子。程序的输出应该是一个解开谜题后的9×9矩阵。 2. 确定算法 数独程序的算法一般有两种,分别是暴力求解和回溯法。 2.1 暴力求解 暴力求解是指从左到右、从上到下依次填数,直到填到空…

    C 2023年5月23日
    00
  • C语言为二维数组分配可能不连续的内存

    为二维数组分配可能不连续的内存空间可以利用数组指针的方式,代码示例如下: // 二维数组指针分配动态内存 int **p; int row = 3, col = 4; p = (int **)malloc(row * sizeof(int *)); for (int i = 0; i < row; ++i) p[i] = (int *)malloc(c…

    C 2023年5月9日
    00
  • Golang 如何解析和生成json

    下面是关于 “Golang 如何解析和生成json” 的完整攻略。 什么是json 首先,我们需要了解什么是JSON。JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它基于JavaScript语法,可以被多种编程语言所支持。在Golang中,需要使用标准库中的encoding/json包来解析和生成JSON格式的数…

    C 2023年5月23日
    00
  • c++11 新特性——智能指针使用详解

    C++11 新特性——智能指针使用详解 在C++中,内存管理一直是一个非常重要的事情,一个常见的错误就是忘记释放先前分配的内存。C++11引入了智能指针,从而使得内存管理更加方便。本文将详细介绍智能指针的使用方法。 智能指针概述 C++中的智能指针是一种RAII(Resource Acquisition Is Initialization)机制的实现,它通过…

    C 2023年5月22日
    00
  • 详解C++图搜索算法之双端队列广搜

    详解C++图搜索算法之双端队列广搜 什么是双端队列广搜 双端队列广搜(Bidirectional Breadth-First Search)是一种图搜索算法,可用于无向图中两点之间的最短路径问题。与传统的广度优先搜索(BFS)相比,双端队列广搜同时从起点和终点出发,通过两端的搜索相遇来实现更快的搜索和更高的效率。 双端队列广搜算法步骤 创建两个队列:起点队列…

    C 2023年5月22日
    00
  • VSCode 配置C++开发环境的方法步骤

    下面是VSCode配置C++开发环境的详细步骤攻略: 步骤一:安装VSCode和MinGW 如果你还没有安装VSCode和MinGW,那么你需要先去官网下载安装。 VSCode官网:https://code.visualstudio.com/ MinGW官网:http://www.mingw.org/ 步骤二:安装C/C++扩展 打开VSCode,在左侧菜单…

    C 2023年5月23日
    00
  • Linux下g++编译与使用静态库和动态库的方法

    下面是针对“Linux下g++编译与使用静态库和动态库的方法”的完整攻略: 1. 编译静态库 1.1 静态库介绍 静态库是在程序编译阶段将库文件的代码全部加入到生成的可执行文件中,因此在程序运行时不需要再去加载这些库文件。另外,同一份静态库可以同时被多个程序使用,节省系统资源。 1.2 编译静态库的方法 编写样例程序如下: // test.cpp #incl…

    C 2023年5月23日
    00
  • Android中各种Time API详细

    Android中各种Time API详细攻略 在Android开发中,时间是一个非常基础的概念,也是涉及到很多核心领域(如UI事件处理、数据同步等)的重要因素。本文将详细介绍在Android中使用各种时间API的方法。 System.currentTimeMillis() System.currentTimeMillis()方法返回当前系统时间(自1970年…

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