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. 程序简介 这个C程序的功能是在命令行上打印出一个倒置的金字塔,金字塔的高度由用户输入。例如,当用户输入数字5时,程序将输出以下金字塔形状: ********* ******* ***** *** * 2. 程序使用方式 在你的计算机上创建一个C源文件,例如pyramid.c。 在文件中写入以下代码:…

    C 2023年5月9日
    00
  • C++快速幂与大数取模算法示例

    C++快速幂与大数取模算法示例 本文主要介绍C++中实现快速幂算法和大数取模算法的示例以及相关代码。快速幂算法可以很好地解决指数较大的幂运算问题,大数取模算法则可以在计算过程中避免数值过大而发生的溢出错误。 快速幂算法原理 快速幂算法是指通过对指数进行二进制分解后,根据分解结果按照乘幂的顺序计算幂运算结果。其本质上是一种分治策略,可以大大减少指数较大情况下的…

    C 2023年5月22日
    00
  • C++ OpenCV实现像素画的示例代码

    首先,实现像素画需要使用C++和OpenCV两个工具,并且需要计算出每个像素块的颜色,然后绘制出来。下面是一份C++ OpenCV实现像素画的示例代码攻略: 准备工作 在开始之前,需要在本地安装好以下工具: C++编译工具(比如Visual Studio) OpenCV图像处理库 安装好之后,需要在代码中引入OpenCV相关头文件,比如: #include …

    C 2023年5月24日
    00
  • C++设计模式之组合模式

    C++设计模式之组合模式攻略 简介 组合模式(Composite Pattern)是一种结构型设计模式。组合模式可以将对象组合成树形结构,表示“部分-整体”的结构层次关系,让客户端统一对待单个对象和组合对象。 结构 组合模式将对象组织成树形结构,有以下三个角色: Component(抽象构件) 抽象构件定义了叶子和容器构件的公共接口,并可以提供一些默认的行为…

    C 2023年5月22日
    00
  • DEVC++实现推箱子小游戏

    DEVC++实现推箱子小游戏攻略 推箱子小游戏是一款非常经典的益智游戏,玩家需要在限定步数内将箱子推到指定位置才能过关。本文将介绍如何使用DEVC++实现推箱子小游戏。 第一步:框架搭建 首先,我们需要创建一个控制台应用程序项目。 打开DEVC++软件,选择“文件”-“新建”-“项目”,进入“新建项目”界面。 在“项目类型”中选择“控制台应用程序”,在“基于…

    C 2023年5月24日
    00
  • 2048小游戏C语言实现代码

    首先,2048小游戏是一款经典的益智游戏,玩家需要通过合并数字达到2048的目标。对于C语言实现,代码可以分为几个部分:界面显示、随机数字生成、输入处理、数字移动和合并、判断游戏是否结束。 界面显示 为了在终端中显示2048的游戏界面,我们需要使用C语言的库函数ncurses。首先,需要安装ncurses库,在Ubuntu系统下使用以下命令安装: sudo …

    C 2023年5月24日
    00
  • C++分析讲解类的静态成员函数如何使用

    当我们需要为一个类定义一个在全局范围内使用的函数时,我们可以使用类的静态成员函数。静态成员函数只能访问静态成员变量,它们没有this指针,所以无法访问非静态成员变量、函数和成员变量的this指针。在C++中,静态成员函数前面加上static关键字即可将其设置为静态成员函数。 如何声明定义静态成员函数 我们可以将静态成员函数声明为public、protecte…

    C 2023年5月23日
    00
  • C语言 数组指针详解及示例代码

    C语言 数组指针详解及示例代码 什么是指针 指针是一种变量,它存储了一个地址。本质上,指针就是一个整数,但是它的类型与所指向对象的类型相同。在C语言中,我们可以通过指针来访问内存中的数据,或者在函数间传递指针来避免在函数之间进行大量的数据复制。 什么是数组指针 数组指针是指向数组的指针。与数组名类似,数组指针也可以被认为是第一个元素的地址。因此,当我们对数组…

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