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
的創建方式
- 通過裸指針創建
boost::shared_ptr<Foo> sp(new Foo());
注意:shared_ptr
接管裸指針,確保不要再手動 delete
。
- 使用
boost::make_shared
(更推薦)
auto sp = boost::make_shared<Foo>(constructor_args...);
- 效率更高,分配一次內存存儲對象和引用計數,減少內存碎片。
- 異常安全。
- 拷貝構造
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;
}
七、使用注意事項
-
避免循環引用
如果兩個對象通過shared_ptr
互相引用,會導致引用計數永遠不為零,造成內存泄漏。解決方法是使用boost::weak_ptr
斷開循環。 -
不要手動 delete
shared_ptr
管理的指針會自動釋放,切勿再手動delete
,否則會雙重釋放。 -
避免與裸指針混用
不建議同一對象既用裸指針又用智能指針管理,易造成懸掛指針。 -
線程安全
Boost的shared_ptr
對引用計數的操作是線程安全的,但對象本身訪問不是線程安全,需額外同步。 -
自定義刪除器
支持在構造時傳入自定義刪除器,用于管理非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
結構有:children
:shared_ptr
容器,擁有子節點parent
:weak_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;
}
重點說明
-
boost::enable_shared_from_this
使對象能夠安全調用shared_from_this()
,獲得自身的shared_ptr
,用于設置子節點的parent
指針。 -
父節點用
weak_ptr
這樣即使子節點持有父節點指針,也不會增加引用計數,避免循環引用。 -
內存自動管理
程序結束時,所有節點都會自動析構,輸出析構信息,證明無內存泄漏。
運行結果示例
Node root 創建
Node child1 創建
Node child2 創建
Node grandchild 創建
rootchild1grandchildchild2
grandchild 的父節點是 child1
Node grandchild 銷毀
Node child1 銷毀
Node child2 銷毀
Node root 銷毀