Boost库智能指针boost::shared_ptr详解和常用场景使用错误示例以及解决方法


1、Boost智能指针 —— boost::shared_ptr 详解


一、什么是 boost::shared_ptr

boost::shared_ptr 是 Boost 库中实现的一个智能指针模板类,用于管理动态分配的对象生命周期,采用引用计数机制。多个 shared_ptr 实例可以共享同一个对象的所有权,引用计数自动管理,最后一个 shared_ptr 销毁时,自动释放对象,避免内存泄漏。


二、定义与包含头文件

#include <boost/shared_ptr.hpp>

定义方式:

boost::shared_ptr<T> ptr;

其中 T 是管理的对象类型。


三、boost::shared_ptr 的创建方式

  1. 通过裸指针创建
boost::shared_ptr<Foo> sp(new Foo());

注意:shared_ptr 接管裸指针,确保不要再手动 delete

  1. 使用 boost::make_shared(更推荐)
auto sp = boost::make_shared<Foo>(constructor_args...);
  • 效率更高,分配一次内存存储对象和引用计数,减少内存碎片。
  • 异常安全。
  1. 拷贝构造
boost::shared_ptr<Foo> sp2 = sp1;

两个智能指针共享同一对象,引用计数增加。


四、与标准容器结合使用

boost::shared_ptr 可以存储在任何标准容器中,最常用的是 std::vector

#include <vector>
#include <boost/shared_ptr.hpp>struct Foo {int x;Foo(int v) : x(v) {}
};int main() {std::vector<boost::shared_ptr<Foo>> vec;vec.push_back(boost::make_shared<Foo>(1));vec.push_back(boost::make_shared<Foo>(2));vec.push_back(boost::make_shared<Foo>(3));for (auto& ptr : vec) {std::cout << ptr->x << std::endl;}
}

优点:

  • 自动管理内存,不用担心容器销毁时忘了释放。
  • 复制容器时引用计数正确递增。
  • 允许多个容器共享相同对象。

五、常用成员函数介绍

函数名说明
use_count()返回当前共享对象的引用计数
reset()释放当前管理对象,指针置空
get()返回裸指针,不影响引用计数
operator*()解引用指针,返回对象引用
operator->()访问对象成员
unique()判断是否唯一拥有者

六、示例代码(完整)

#include <iostream>
#include <vector>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>struct Foo {int data;Foo(int d) : data(d) { std::cout << "Foo ctor " << data << std::endl; }~Foo() { std::cout << "Foo dtor " << data << std::endl; }void show() { std::cout << "Data: " << data << std::endl; }
};int main() {std::vector<boost::shared_ptr<Foo>> vec;// 创建并添加智能指针到容器vec.push_back(boost::make_shared<Foo>(10));vec.push_back(boost::make_shared<Foo>(20));std::cout << "引用计数: " << vec[0].use_count() << std::endl; // 1// 共享指针拷贝boost::shared_ptr<Foo> sp = vec[0];std::cout << "引用计数: " << vec[0].use_count() << std::endl; // 2sp->show();// 遍历容器访问对象for (const auto& p : vec) {p->show();}// 重置指针sp.reset();std::cout << "引用计数: " << vec[0].use_count() << std::endl; // 1return 0;
}

七、使用注意事项

  1. 避免循环引用
    如果两个对象通过shared_ptr互相引用,会导致引用计数永远不为零,造成内存泄漏。解决方法是使用 boost::weak_ptr 断开循环。

  2. 不要手动 delete
    shared_ptr管理的指针会自动释放,切勿再手动 delete,否则会双重释放。

  3. 避免与裸指针混用
    不建议同一对象既用裸指针又用智能指针管理,易造成悬挂指针。

  4. 线程安全
    Boost的 shared_ptr 对引用计数的操作是线程安全的,但对象本身访问不是线程安全,需额外同步。

  5. 自定义删除器
    支持在构造时传入自定义删除器,用于管理非new创建的资源。

boost::shared_ptr<FILE> filePtr(fopen("data.txt", "r"), fclose);

八、总结

  • boost::shared_ptr 是强大的共享所有权智能指针。
  • 支持拷贝,自动管理引用计数。
  • 能与 STL 容器完美配合使用,管理动态资源更安全。
  • 需注意循环引用问题。
  • C++11之后建议优先使用标准库的 std::shared_ptr,但Boost版本在老项目和特定场景仍很重要。

2、循环引用问题和解决方案示例1


1. 循环引用问题示例(导致内存泄漏)

两个对象互相用 shared_ptr 持有对方,引用计数永远不为零,析构函数不会被调用。

#include <iostream>
#include <boost/shared_ptr.hpp>struct B; // 前置声明struct A {boost::shared_ptr<B> ptrB;~A() { std::cout << "A析构" << std::endl; }
};struct B {boost::shared_ptr<A> ptrA;~B() { std::cout << "B析构" << std::endl; }
};int main() {boost::shared_ptr<A> a(new A);boost::shared_ptr<B> b(new B);a->ptrB = b;b->ptrA = a;// a 和 b 超出作用域后不会析构,造成内存泄漏return 0;
}

运行结果:

(无析构信息,内存泄漏)

2. 解决方案:使用 boost::weak_ptr 断开循环引用

把其中一个 shared_ptr 改成 weak_ptr,不会增加引用计数,从而允许正常析构。

#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>struct B; // 前置声明struct A {boost::shared_ptr<B> ptrB;~A() { std::cout << "A析构" << std::endl; }
};struct B {boost::weak_ptr<A> ptrA;  // 用 weak_ptr 代替 shared_ptr~B() { std::cout << "B析构" << std::endl; }
};int main() {boost::shared_ptr<A> a(new A);boost::shared_ptr<B> b(new B);a->ptrB = b;b->ptrA = a;  // 赋值给 weak_ptr 不增加引用计数std::cout << "a.use_count() = " << a.use_count() << std::endl; // 1std::cout << "b.use_count() = " << b.use_count() << std::endl; // 2// 访问 weak_ptr 对象需先 lock()if (auto lockedA = b->ptrA.lock()) {std::cout << "访问成功,a对象还存在" << std::endl;} else {std::cout << "a对象已销毁" << std::endl;}return 0;
}

运行结果:

a.use_count() = 1
b.use_count() = 2
访问成功,a对象还存在
B析构
A析构

3. 说明

  • boost::weak_ptr 不控制对象生命周期,不会增加引用计数。
  • 使用 weak_ptr::lock() 获取 shared_ptr,判断对象是否仍存在。
  • weak_ptr 用于观察对象,避免循环引用导致的内存泄漏。
  • 循环引用一般是父子、互相持有关系时的常见问题。

3、复杂父子关系 & 图结构循环引用示例2

假设有一棵树或者有向图结构,节点互相引用,父节点持有子节点的强引用,子节点持有父节点的弱引用,防止循环引用。


示例说明

  • Node 结构有:

    • childrenshared_ptr 容器,拥有子节点
    • parentweak_ptr,指向父节点,避免循环引用
  • 结构可以扩展成任意多层复杂关系。


代码示例

#include <iostream>
#include <vector>
#include <string>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>struct Node {std::string name;boost::weak_ptr<Node> parent;                    // 指向父节点的弱引用std::vector<boost::shared_ptr<Node>> children;   // 拥有子节点的强引用Node(const std::string& n) : name(n) {std::cout << "Node " << name << " 创建" << std::endl;}~Node() {std::cout << "Node " << name << " 销毁" << std::endl;}// 添加子节点void addChild(boost::shared_ptr<Node> child) {child->parent = shared_from_this(); // 这里需要启用enable_shared_from_thischildren.push_back(child);}// 输出结构,递归void printTree(int depth = 0) {std::cout << std::string(depth * 2, ' ') << name << std::endl;for (auto& child : children) {child->printTree(depth + 1);}}
};// 使 Node 支持 shared_from_this()
struct NodeWrapper : public Node, public boost::enable_shared_from_this<Node> {using Node::Node;using boost::enable_shared_from_this<Node>::shared_from_this;
};int main() {// 创建根节点boost::shared_ptr<NodeWrapper> root = boost::make_shared<NodeWrapper>("root");// 创建子节点boost::shared_ptr<NodeWrapper> child1 = boost::make_shared<NodeWrapper>("child1");boost::shared_ptr<NodeWrapper> child2 = boost::make_shared<NodeWrapper>("child2");// 构建树结构root->addChild(child1);root->addChild(child2);boost::shared_ptr<NodeWrapper> grandchild = boost::make_shared<NodeWrapper>("grandchild");child1->addChild(grandchild);// 输出树结构root->printTree();// 访问父节点if (auto p = grandchild->parent.lock()) {std::cout << grandchild->name << " 的父节点是 " << p->name << std::endl;} else {std::cout << "父节点不存在" << std::endl;}return 0;
}

重点说明

  1. boost::enable_shared_from_this
    使对象能够安全调用 shared_from_this(),获得自身的 shared_ptr,用于设置子节点的 parent 指针。

  2. 父节点用 weak_ptr
    这样即使子节点持有父节点指针,也不会增加引用计数,避免循环引用。

  3. 内存自动管理
    程序结束时,所有节点都会自动析构,输出析构信息,证明无内存泄漏。


运行结果示例

Node root 创建
Node child1 创建
Node child2 创建
Node grandchild 创建
rootchild1grandchildchild2
grandchild 的父节点是 child1
Node grandchild 销毁
Node child1 销毁
Node child2 销毁
Node root 销毁

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

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

相关文章

科学分析指南,如何快速找到并清理磁盘的无用文件

随着时间的推移&#xff0c;系统中会积累大量的临时文件、缓存文件、不再需要的安装包或其他大型文件。磁盘清理可以删除这些不必要的文件&#xff0c;从而释放宝贵的磁盘空间。它无需安装&#xff0c;插上 U 盘就能直接使用。只需勾选需要扫描的磁盘&#xff0c;点击“开始分析…

Laravel 系统版本查看及artisan管理员密码找回方法针对各个版本通用方法及原理-优雅草卓伊凡

Laravel 系统版本查看及artisan管理员密码找回方法针对各个版本通用方法及原理-优雅草卓伊凡一、查看 Laravel 版本的方法优雅草蜻蜓T会议系统专业版 最近又有客户要了&#xff0c;但是发现 密码不对 管理员账户密码不对&#xff0c;卓伊凡必须处理下&#xff0c;这里顺便讲解密…

针对大规模语言模型的上下文工程技术调研与总结(翻译并摘要)

针对大规模语言模型的上下文工程技术调研与总结声明摘要部分翻译介绍部分翻译相关工作部分翻译并摘要为什么使用上下文工程&#xff08;翻译并摘要&#xff09;基础组件&#xff08;翻译并摘要&#xff09;RAG&#xff08;翻译并摘要简单介绍一下个人认为比较好的技术&#xff…

QT配置Quazip外部库

1.下载QuaZip源码网址&#xff1a;https://sourceforge.net/projects/quazip/  注&#xff1a;下载->解压->打开.pro文件2.编译QuaZip源码2.1配置zlib注&#xff1a;QuaZip需zlib的支持&#xff0c;我们需要引用zlib找到本地安装Qt目录下zlib目录&#xff1a;注&#x…

从C++开始的编程生活(4)——类的定义、访问限定符、类域、类的实例化和this指针

前言 本系列文章承接C语言的学习&#xff0c;需要有C语言的基础才能学会哦~ 第3篇主要讲的是有关于C的类的定义、访问限定符、类域、类的实例化和this指针。 C才起步&#xff0c;都很简单呢&#xff01; 目录 前言 类 基本语法 访问限定符 基本语法 类域 类的实例化 内…

AD域控制器虚拟化的安全加固最佳实践

以下是AD域控制器虚拟化安全加固的7项核心实践&#xff0c;结合最新Windows Server 2022特性与虚拟化环境需求&#xff1a;基础架构强化‌ 采用静态IP分配并确保所有域控节点DNS指向主DC&#xff08;如192.168.1.10&#xff09;‌ 虚拟机模板需预配置林/域功能级别为Windows Se…

java解析nc气象数据

1.1pom.xml<dependency><groupId>edu.ucar</groupId><artifactId>netcdfAll</artifactId><version>5.4.1</version></dependency>1.2 netcdf使用/** param type 0 ,1, 2 wind 1 or 2 其他 0 .* return Map* */public Map i…

STC8H8K64U SKDIP28芯片频率占空比PWM波形

/****PWM输出任意周期占空比波形*******/ #include "STC8H.h" // #include "intrins.h" // #define uchar unsigned char // #define uint unsig…

【RK3576】【Android14】USB开发调试

获取更多相关的【RK3576】【Android14】驱动开发&#xff0c;可收藏系列博文&#xff0c;持续更新中&#xff1a; 【RK3576】Android 14 驱动开发实战指南 硬件接口 RK3576支持两个USB3.0控制器 驱动开发 dts配置 在“Android14/kernel-6.1/arch/arm64/boot/dts/rockchip/rk…

20. TaskExecutor与ResourceManager心跳

20. TaskExecutor与ResourceManager心跳 现在&#xff0c;需要回过头看 ResourceManager是如何产生心跳管理服务的。cluster.initializeServices 方法的 heartbeatServices createHeartbeatServices(configuration);产生一个 HeartbeatServicesImpljobmanager的 resourceManag…

OS19.【Linux】进程状态(上)

目录 1.情景引入 2.操作系统学科对进程状态的分类 运行状态 基于时间片的轮转调度算法 阻塞状态 等待IO设备的例子 等待其他进程中需要获取的数据 进程唤醒 挂起状态(全称为阻塞挂起状态) 简单谈谈虚拟内存管理 就绪状态 笔面试题 3.Linux对进程状态的分类 R和S状…

如何优雅地修改项目的 Android 版本(API 级别)

引言 在 Android 开发的日常迭代中&#xff0c;我们经常需要升级或降级项目的 minSdkVersion、targetSdkVersion 与 compileSdkVersion。升级可以解锁新特性和性能优化&#xff1b;降级则可能为了兼容旧机型或快速验证问题。本文将手把手演示在 Android Studio 里修改 Android …

GNU Radio多类信号多种参数数据集生成技巧

参考我的这篇博客&#xff0c;我想自制一个多信号数据集&#xff1a; 【多雷达信号硬件模拟】 3台USRP1台VSG信号发生器模拟多雷达信号&#xff0c;1台USRP产生高斯噪声模拟更多信道环境&#xff0c;1台USRP采集信号 需要在多个波段对四种信号进行参数设置&#xff0c;带宽有…

Ansible + Shell 服务器巡检脚本

脚本概述这是一个用于服务器日常巡检的 Shell 脚本&#xff0c;主要功能包括&#xff1a;检查多台主机的网络连通性 监控CPU、内存和磁盘使用率 生成详细的巡检报告 通过企业微信发送告警通知核心技术点1. 主机批量管理使用Ansible工具远程执行命令和脚本 通过主机…

Linux-rpm和yum

一、RPMRPM&#xff08;Red Hat Package Manager&#xff09;是一个用于管理 Red Hat 系列 Linux 发行版&#xff08;如 RHEL、CentOS、Fedora&#xff09;软件包的工具。RPM 允许用户以统一的格式来安装、卸载、升级和查询软件包。它是 .rpm 文件的主要工具&#xff0c;后缀名…

手推OpenGL相机的正交投影矩阵和透视投影矩阵(附源码)

概述计算OpenGL的正交投影矩阵和透视投影矩阵是有现成函数的。自己手推不是为了重复造轮子。手推一遍&#xff0c;可以极大的加强对这两个矩阵的理解。同时也可以满足一下自己求知欲。正交投影矩阵手推正交投影矩阵源码 WGMatrix4x4 WGMatrix4x4::BuildOrtho(double l, double …

【跨国数仓迁移最佳实践2】MaxCompute SQL执行引擎对复杂类型处理全面重构,保障客户从BigQuery平滑迁移

本系列文章将围绕东南亚头部科技集团的真实迁移历程展开&#xff0c;逐步拆解 BigQuery 迁移至 MaxCompute 过程中的关键挑战与技术创新。本篇为第二篇&#xff0c;跨国数仓迁移背后 MaxCompute 的统一存储格式创新。 注&#xff1a;客户背景为东南亚头部科技集团&#xff0c;…

react(基础篇)

React由Meta公司研发&#xff0c;用于构建Web和原生交互界面的库。 React 官方中文文档 查看JSX &#xff08;一&#xff09;React组件 用户界面的一部分&#xff0c;通俗的来讲&#xff0c;最小的元素组成的单元&#xff0c;可以实现部分逻辑与功能 房子的门就可以看成一个…

数据结构-哈希表(一)哈希函数、哈希表介绍、优缺点

哈希表 哈希函数哈希表使用了哈希函数来完成key到地址的快速映射&#xff0c;所以在了解哈希表之前&#xff0c;需要先明白哈希函数的概念和特点。 哈希函数的定义 哈希函数 哈希函数是一种将任意长度输入的数据&#xff0c;转换成固定长度输出的算法哈希函数H可以表示为yH(x) …

Shader开发(一)什么是渲染

前言在现代游戏开发和计算机图形学领域&#xff0c;渲染技术是连接虚拟世界与视觉呈现的关键桥梁。无论你是刚接触图形编程的新手&#xff0c;还是希望深入理解渲染原理的开发者&#xff0c;掌握渲染的核心概念都是必不可少的第一步。什么是渲染&#xff1f;渲染&#xff08;Re…