当前位置: 首页 > 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://hardily .wanhuigw.com
http://consummator .wanhuigw.com
http://griselda .wanhuigw.com
http://confiscator .wanhuigw.com
http://imperil .wanhuigw.com
http://flam .wanhuigw.com
http://seminole .wanhuigw.com
http://deambulation .wanhuigw.com
http://abaya .wanhuigw.com
http://tinworks .wanhuigw.com
http://sensibility .wanhuigw.com
http://picky .wanhuigw.com
http://iniquity .wanhuigw.com
http://developable .wanhuigw.com
http://sexisyllable .wanhuigw.com
http://genupectoral .wanhuigw.com
http://quadriplegic .wanhuigw.com
http://avalement .wanhuigw.com
http://hoya .wanhuigw.com
http://spathic .wanhuigw.com
http://proboscidean .wanhuigw.com
http://uppie .wanhuigw.com
http://petrous .wanhuigw.com
http://scopy .wanhuigw.com
http://freebsd .wanhuigw.com
http://permease .wanhuigw.com
http://indurative .wanhuigw.com
http://shaft .wanhuigw.com
http://sociosexual .wanhuigw.com
http://silicification .wanhuigw.com
http://www.dtcms.com/a/290872.html

相关文章:

  • 软件测试 —— A / 入门
  • 数据结构 之 【排序】(直接插入排序、希尔排序)
  • 基于 Nginx 搭建 OpenLab 多场景 Web 网站:从基础配置到 HTTPS 加密全流程
  • Nginx IP授权页面实现步骤
  • Grok网站的后端语言是php和Python2.7
  • Python 变量赋值与切片语法(in-place 修改 vs 重新赋值)
  • 《画布角色的双重灵魂:解析Canvas小游戏中动画与碰撞的共生逻辑》
  • 状压DP学习笔记[浅谈]
  • 计算机网络:概述层---计算机网络的性能指标
  • IFN影视官网入口 - 4K影视在线看网站|网页|打不开|下载
  • 算法训练营DAY37 第九章 动态规划 part05
  • Linux开发⊂嵌入式开发
  • 复制docker根目录遇到的权限问题
  • Mac安装Typescript报错
  • macOS 上安装 Kubernetes(k8s)
  • 深度学习-常用环境配置
  • 基于R语言的分位数回归技术应用
  • next.js刷新页面时二级菜单展开状态判断
  • Java 通过 HttpURLConnection发送 http 请求
  • CG-04 翻斗式雨量传感器 分辨率0.1mm,0.2mm可选择 金属材质
  • 数据结构自学Day11-- 排序算法
  • 使用 Longformer-base-4096 进行工单问题分类
  • Redis进阶--缓存
  • Ubuntu 22.04 安装 MySQL 8.0 完整步骤文档
  • 计算机网络中:传输层和网络层之间是如何配合的
  • 7月21日星期一今日早报简报微语报早读
  • 计算机史前时代:从原始计数到机械曙光
  • 计算机发展史:集成电路时代的微缩革命
  • Android 实例 - 分页器封装实现(上一页按钮、下一页按钮、当前页码 / 总页数、每页条数、总记录数)
  • 本地部署AI新选择!LocalAI+cpolar轻松实现隐私安全的远程访问