当前位置: 首页 > news >正文

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.lryc.cn/news/594666.html

相关文章:

  • 如何防止QQ浏览器录屏,盗录视频资源?
  • Pytorch02:深度学习基础示例——猫狗识别
  • MySQL(05) mysql锁,MVCC、Innodb行锁
  • 网络协议与层次对应表
  • Spring Boot 集成 RabbitMQ:普通队列、延迟队列与死信队列全解析
  • 我的网页聊天室设计
  • Python100个库分享第38个—lxml(爬虫篇)
  • sky-take-out项目中Redis的使用
  • 【Linux】Prometheus 监控 Kafka 集群
  • 基于大数据的旅游推荐系统 Python+Django+Hive+Vue.js
  • 关于 URL 中 “+“ 号变成空格的问题
  • 机器学习对词法分析、句法分析、浅层语义分析的积极影响
  • 人工智能真的能编程吗?研究勾勒出自主软件工程的障碍
  • [Python] -项目实战10- 用 Python 自动化批量重命名文件
  • 识别并计算滑块距离
  • 远程登录服务器黑屏如何处理?
  • 日历类生辰八字九九三伏入梅出梅算法
  • 某日在某个月份中不存在导致软件出现异常的问题排查(判断闰年以及月份中的天数,附完整源码)
  • 编译支持cuda硬件加速的ffmpeg
  • cuda编程笔记(9)--使用 Shared Memory 实现 tiled GEMM
  • Linux进程核心机制:状态、优先级与上下文切换详解
  • 亚马逊自养号测评实战指南:从环境搭建到安全提排名
  • 微信小程序服务端快速对接指南(java版)
  • 添加状态信息
  • Docker实践:使用Docker部署blog轻量级博客系统
  • Python Matplotlib中的fontdict参数说明
  • 前后端分离项目进阶1---后端
  • 易语言+懒人精灵/按键中控群控教程(手机、主板机、模拟器通用)
  • 子网划分核心原理 (网络原理1)
  • Windows Server2022下使用SQL Server2019开发版搭建高可用集群