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
英文地址,請注明出處:http://en.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…