【C++】std::shared_ptr智能指针详解和示例

news2024/9/9 0:18:44

在C++中,智能指针是一种用于自动管理动态分配内存的机制,旨在减少内存泄漏和野指针的风险。std::shared_ptr 是C++标准库提供的几种智能指针之一,它通过共享所有权的机制来管理动态分配的对象。本文将详细解析 std::shared_ptr 的工作原理、特性,并通过一个应用案例来展示其在实际开发中的使用。

  • std::shared_ptr 工作原理
  • 主要特性
    • 成员函数
    • 注意事项
  • 应用案例
    • 示例1
    • 示例2
  • 结论

std::shared_ptr 工作原理

std::shared_ptr 使用一个内部计数器(通常称为控制块)来跟踪有多少个 std::shared_ptr 实例共享同一个对象。每当一个新的 std::shared_ptr 被创建并指向某个对象时,计数器就会递增;当 std::shared_ptr 被销毁或重置时,计数器就会递减。当计数器减至零时,表明没有 std::shared_ptr 实例再共享该对象,此时对象将被自动删除,释放其所占用的内存。

主要特性

自动内存管理:当 std::shared_ptr 的实例被销毁时(例如,离开作用域时),它会检查其所指向的对象是否还有其他 std::shared_ptr 实例在共享所有权。如果没有其他实例,则自动删除该对象。
共享所有权:多个 std::shared_ptr 实例可以指向同一个对象,每个实例都拥有该对象的一部分所有权。当所有权计数变为零时,对象被删除。
线程安全:在增加或减少所有权计数时,std::shared_ptr 提供了必要的同步机制,以确保操作的原子性。但是,这并不意味着对共享对象的操作本身是线程安全的。
自定义删除器:可以指定一个自定义的删除器,以便在删除对象时执行特定的清理操作。

成员函数

get():返回原始指针。
reset():重置 std::shared_ptr,可以选择指向一个新对象或变为空。
use_count():返回共享此对象的 std::shared_ptr 实例的数量(线程安全)。
unique():如果 use_count() 返回 1,则返回 true,表示当前 std::shared_ptr 是唯一指向其对象的实例。
swap():交换两个 std::shared_ptr 实例的内容。

注意事项

当使用 reset() 方法将 std::shared_ptr 重置为另一个对象或 nullptr 时,如果原对象没有其他 std::shared_ptr 实例在共享所有权,则原对象将被删除。
如果 std::shared_ptr 持有的是指向动态分配数组的指针,则应该使用 std::shared_ptr<T[]>(C++11 引入的数组特化)或自定义删除器来确保数组被正确删除。
循环引用是 std::shared_ptr 的一个潜在问题,它会导致内存泄漏。循环引用发生在两个或多个 std::shared_ptr 实例相互引用,从而阻止对方被销毁。为了解决这个问题,可以使用 std::weak_ptr。

应用案例

示例1

假设我们正在开发一个图形库,其中包含了多种图形元素(如圆形、矩形等),这些图形元素需要被动态创建并在需要时被自动销毁。我们可以使用 std::shared_ptr 来管理这些图形元素的内存。

以下是一个简化的应用案例:

#include <iostream>
#include <memory>
#include <vector>

class Shape {
public:
    virtual void draw() const = 0;
   
};

class Circle : public Shape {
    int radius;
public:
    Circle(int r) : radius(r) {}
    void draw() const override {
        std::cout << "Drawing a circle with radius " << radius << std::endl;
    }
};

class Rectangle : public Shape {
    int width, height;
public:
    Rectangle(int w, int h) : width(w), height(h) {}
    void draw() const override {
        std::cout << "Drawing a rectangle with width " << width << " and height " << height << std::endl;
    }
};

int main() {
    std::vector<std::shared_ptr<Shape>> shapes;

    // 动态创建图形元素并添加到shapes中
    shapes.push_back(std::make_shared<Circle>(5));
    shapes.push_back(std::make_shared<Rectangle>(10, 20));

    // 遍历并绘制所有图形
    for (const auto& shape : shapes) {
        shape->draw();
    }

    // 当shapes的作用域结束时,所有图形元素将被自动销毁

    return 0;
}

在这个例子中,我们定义了一个 Shape 基类和两个派生类 Circle 和 Rectangle。我们使用 std::vector<std::shared_ptr> 来存储不同类型的图形元素,并利用 std::make_shared 来创建 std::shared_ptr 实例。这样,我们就能够自动管理这些图形元素的内存,而无需担心内存泄漏或野指针的问题。

在这里插入图片描述

示例2

#include <iostream>
#include <memory>
#include <vector>

class Node {
public:
    int value;
    std::vector<std::shared_ptr<Node>> neighbors;

    Node(int val) : value(val) {}

    void addNeighbor(std::shared_ptr<Node> neighbor) {
        neighbors.push_back(neighbor);
    }

    // 打印节点的值及其邻居的值
    void printNeighbors() const {
        std::cout << "Node " << value << " neighbors: ";
        for (const auto& neighbor : neighbors) {
            std::cout << neighbor->value << " ";
        }
        std::cout << std::endl;
    }
};

int main() {
    // 创建节点
    std::shared_ptr<Node> node1 = std::make_shared<Node>(1);
    std::shared_ptr<Node> node2 = std::make_shared<Node>(2);
    std::shared_ptr<Node> node3 = std::make_shared<Node>(3);

    // 添加邻居
    node1->addNeighbor(node2);
    node1->addNeighbor(node3);
    node2->addNeighbor(node1);
    node3->addNeighbor(node1);

    // 打印结果
    node1->printNeighbors(); // 输出 Node 1 的邻居
    node2->printNeighbors(); // 输出 Node 2 的邻居
    node3->printNeighbors(); // 输出 Node 3 的邻居

    // 注意:这里没有循环引用问题,因为每个节点都持有其邻居的弱引用(实际上这里是强引用,但在更复杂的图中可能会使用弱引用来避免循环引用)

    // 当这些 shared_ptr 离开作用域时,它们指向的 Node 对象将被自动删除
    return 0;
}

在这里插入图片描述

结论

std::shared_ptr 是C++标准库中一个强大且灵活的工具,它通过共享所有权的机制来自动管理动态分配的内存。在实际开发中,我们可以利用 std::shared_ptr 来简化内存管理,减少内存泄漏的风险。然而,我们也需要注意循环引用的问题,并在必要时使用 std::weak_ptr 来打破循环引用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1958403.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【电路笔记】-共源JFET放大器

共源JFET放大器 文章目录 共源JFET放大器1、概述2、共源JFET放大器3、JFET放大器电流和功率增益共源JFET放大器使用结场效应晶体管作为其主要有源器件,提供高输入阻抗特性。 1、概述 普通源JFET放大器与共射极BJT放大器相比有一个重要优点,即FET具有极高的输入阻抗,再加上低…

工业三防平板,高效能与轻便性的结合

在当今数字化、智能化的工业时代&#xff0c;工业三防平板作为一种创新的设备&#xff0c;正以其独特的优势在各个领域发挥着重要作用。它不仅具备高效能的处理能力&#xff0c;还拥有出色的轻便性&#xff0c;为工业生产和管理带来了前所未有的便利。 一、高效能的核心动力 工…

2024年中职云计算实验室建设及云计算实训平台整体解决方案

随着信息技术的飞速发展&#xff0c;云计算作为新一代信息技术的核心&#xff0c;正逐步渗透到各行各业&#xff0c;成为推动数字化转型的重要力量。为了适应这一趋势&#xff0c;中职教育作为技能型人才培养的重要阵地&#xff0c;亟需加强云计算实验室建设与云计算实训平台的…

web,apache,nginx

web基本概念和常识 Web:为用户提供的一种在互联网上浏览信息的服务&#xff0c;Web 服务 是动态的、可交 互的、跨平台的和图形化的。 Web 服务为用户提供各种互联网服务&#xff0c;这些服务包括信息浏览服务&#xff0c;以及各种交互式服务&#xff0c;包括聊天、购物、学习…

泰迪智能科技大数据实验室——陕西省高校合作成功案例

近年来&#xff0c;陕西省紧跟国家大数据发展战略&#xff0c;积极推进大数据产业发展。在政策扶持、产业布局、技术创新等方面取得显著成效。泰迪智能科技大数据实验室立足陕西&#xff0c;携手西安邮电大学、西安财经大学、陕西科技大学镐京学院、宝鸡文理学院、渭南师范学院…

编译期链接时共享库搜索路径优先级实验

编译期链接时共享库搜索路径优先级实验 前言实验环境目录说明准备工作单独测试不配置路径默认路径LIBRARY_PATH-L 优先级测试默认路径和LIBRARY_PATH-L和默认路径 DEBUG模式编译器配置详细信息链接器详细信息DEBUG总结验证 默认路径>LIBRARY_PATH原因附录库文件源码主程序源…

bugku-web-ctf-变量1

<?php error_reporting(0); include "flag1.php"; highlight_file(__file__); if(isset($_GET[args])){$args $_GET[args];if(!preg_match("/^\w$/",$args)){die("args error!");}eval("var_dump($$args);"); } ?> error_r…

Apache、nginx

一、Web 1、概述 Web&#xff1a;为⽤户提供的⼀种在互联⽹上浏览信息的服务&#xff0c;Web 服务是动态的、可交互的、跨平台的和图形化的。 Web 服务为⽤户提供各种互联⽹服务&#xff0c;这些服务包括信息浏览服务&#xff0c;以及各种交互式服务&#xff0c;包括聊天、购物…

React基础知识 精简全面 推荐

这篇博文主要对一些刚入门react框架的同学&#xff0c;以及对react基本知识进行巩固的&#xff0c;最后就是精简一下基本知识&#xff0c;以方便自己查看&#xff0c;感谢参考&#xff0c;有问题评论区交流&#xff0c;谢谢。 目录 1.JSX 2.Props 和 State 3.组件生命周期…

“八股文”在实际工作中是助力、阻力还是空谈?

程序员面试中的“八股文”&#xff1a;助力、阻力还是空谈&#xff1f; 在当前的技术行业&#xff0c;程序员的招聘面试过程中频繁出现对“八股文”的考核。“八股文”通常指的是关于编程知识的标准化回答&#xff0c;这些问题在网络上大量流传&#xff0c;并被求职者反复背诵…

Socket通信(C++)

文章目录 什么是SocketSocket通信过程C Socket通信APIint socket(int domain, int type, int protocol);int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);struct sockaddrstruct sockaddr_unstruct sockaddr_in / struct sockaddr_in6 int connect(int …

IP Fabric三层路由

IP Fabric指的是在IP网络基础上建立起来的Overlay隧道技术。即为基于胖树的SpineLeaf拓扑结构的IP Fabric组网图。 在这种组网方式中&#xff0c;任何两台服务器间的通信不超过3台设备&#xff0c;每个Spine和Leaf节点全互连&#xff0c;可以方便地通过扩展Spine节点来实现网络…

Godot学习笔记6——数组和for

一、定义一个数组 在Godot中&#xff0c;定义一个数组的关键字也是“var”&#xff0c;数组里面的内容使用方括号括起来。在没有限定类型时&#xff0c;我们可以放入任何类型的数据&#xff1a; 我们甚至可以将另一个数组放入此数组中&#xff1a; 和其他类型的变量类似&#…

【数据结构】包装类泛型

1.包装类 在 Java 中&#xff0c;由于基本类型不是继承自 Object &#xff0c;为了在泛型代码中可以支持基本类型&#xff0c; Java 给每个基本类型都对应了 一个包装类型。 1.1.基本的数据类型对应的包装类 1.2装箱和拆箱 //装箱int a10;Integer cInteger.valueOf(a);System.…

鸿蒙应用框架开发【简单时钟】 UI框架

简单时钟 介绍 本示例通过使用ohos.display接口以及Canvas组件来实现一个简单的时钟应用。 效果预览 使用说明 1.界面通过setInterval实现周期性实时刷新时间&#xff0c;使用Canvas绘制时钟&#xff0c;指针旋转角度通过计算得出。 例如&#xff1a;"2 * Math.PI / …

Synchronized的锁升级过程是怎样的?

文章目录 一、Synchronized的使用1、修饰实例方法2、修饰静态方法3、修饰代码块4、总结&#xff1a; 二、Monitor1、Java对象头1.1 32 位虚拟机的对象头1.2 64位虚拟机的对象头 2、Mark Word 结构3、Moniter4、Synchronized 字节码5、轻量级锁6、锁膨胀7、自旋优化8、偏向锁9、…

Python for循环迭代原理(迭代器 Iterator)

在使用Python时&#xff0c;我们经常会使用for循环来访问容器对象&#xff08;列表、字符、字典等&#xff09;中的元素。其幕后实际是通过迭代协议来完成的&#xff0c;迭代是一种依次访问对象中元素的方式&#xff0c;for循环在对象上调用iter()函数生成一个迭代器&#xff0…

从后端开发视角认识向量数据库

以ChatGPT为代表的大语言模型应用自问世以来已经火了好几年。在这期间国内外类似产品层出不穷&#xff0c;甚至公司内部团队都开发了好几个AI小助手。刚好最近看了几篇关于大语言模型应用开发的文章&#xff0c;借此了解了一下应用层面的基本知识&#xff0c;也算是接触到了大语…

轻松入门Linux—CentOS,直接拿捏 —/— <2>

一 、权限问题详细讲解 读写的权限可以分别写成 r, w, x 总共有九个权限&#xff0c;可以分组三大组分别是&#xff1a; user&#xff1a;当前文件所属用户的权限 group&#xff1a;与当前文件所属用户同一组的用户权限 others&#xff1a;其他用户的权限 故使用 u, g, o 来代表…

Qt Creator 与 ESP-IDF QEMU 模拟器使用指南

标题: Qt Creator 与 ESP-IDF QEMU 模拟器使用指南 概要: 本文为开发者提供了使用 Qt Creator 和 ESP-IDF QEMU 模拟器进行 ESP32 开发的详细指南&#xff0c;包括环境准备、项目创建和编译、模拟器设置、编程和调试等方面的内容。通过本指南&#xff0c;可以快速上手 Qt Crea…