Serie: C++
cpp
396 líneas
· Actualizado 2026-04-03
shared_weak_ptr.cpp
C++/Part4_現代CPP/Ch16_記憶體管理與智慧指標/shared_weak_ptr.cpp
// ============================================================
// Ch16 — shared_ptr 與 weak_ptr 完整示範
// 編譯:g++ -std=c++17 -Wall -o shared_weak_ptr shared_weak_ptr.cpp
// ============================================================
#include <iostream>
#include <memory>
#include <string>
#include <vector>
// ── 輔助函式 ──
void section(const std::string& title) {
std::cout << "\n========================================\n";
std::cout << " " << title << "\n";
std::cout << "========================================\n";
}
// ── 示範用類別 ──
class Resource {
public:
Resource(const std::string& name) : name_(name) {
std::cout << " Resource(\"" << name_ << "\") 建構\n";
}
~Resource() {
std::cout << " Resource(\"" << name_ << "\") 解構\n";
}
void use() const {
std::cout << " 使用 Resource(\"" << name_ << "\")\n";
}
const std::string& name() const { return name_; }
private:
std::string name_;
};
// ── 循環參考問題示範 ──
// 有循環參考問題的版本
class BadNode {
public:
std::string name;
std::shared_ptr<BadNode> next;
std::shared_ptr<BadNode> prev; // 循環參考!
BadNode(const std::string& n) : name(n) {
std::cout << " BadNode(\"" << name << "\") 建構\n";
}
~BadNode() {
std::cout << " BadNode(\"" << name << "\") 解構\n";
}
};
// 用 weak_ptr 修正的版本
class GoodNode {
public:
std::string name;
std::shared_ptr<GoodNode> next;
std::weak_ptr<GoodNode> prev; // 用 weak_ptr 打破循環
GoodNode(const std::string& n) : name(n) {
std::cout << " GoodNode(\"" << name << "\") 建構\n";
}
~GoodNode() {
std::cout << " GoodNode(\"" << name << "\") 解構\n";
}
};
// ── 觀察者模式中的 weak_ptr ──
class Subject {
public:
explicit Subject(const std::string& name) : name_(name) {}
const std::string& name() const { return name_; }
private:
std::string name_;
};
class Observer {
public:
explicit Observer(const std::string& name) : name_(name) {}
void watch(std::shared_ptr<Subject> subject) {
subject_ = subject; // 存為 weak_ptr,不影響生命週期
}
void report() const {
if (auto sp = subject_.lock()) {
std::cout << " Observer(" << name_ << "): 觀察到 Subject(\""
<< sp->name() << "\") 仍然存在\n";
} else {
std::cout << " Observer(" << name_ << "): Subject 已被釋放\n";
}
}
private:
std::string name_;
std::weak_ptr<Subject> subject_;
};
int main() {
std::cout << "===== Ch16:shared_ptr 與 weak_ptr 示範 =====\n";
// ──────────────────────────────────────────
// 1. 建立 shared_ptr
// ──────────────────────────────────────────
section("1. 建立 shared_ptr");
{
// make_shared(推薦)
auto sp1 = std::make_shared<int>(42);
std::cout << "make_shared<int>: " << *sp1 << "\n";
std::cout << "use_count: " << sp1.use_count() << "\n";
// 直接建構(不推薦)
std::shared_ptr<double> sp2(new double(3.14));
std::cout << "shared_ptr(new): " << *sp2 << "\n";
// 自訂類別
auto sp3 = std::make_shared<Resource>("Alpha");
sp3->use();
std::cout << "use_count: " << sp3.use_count() << "\n";
}
// ──────────────────────────────────────────
// 2. 共享擁有權與參考計數
// ──────────────────────────────────────────
section("2. 共享擁有權與參考計數");
{
auto sp1 = std::make_shared<Resource>("Beta");
std::cout << "建立後 use_count: " << sp1.use_count() << "\n";
{
auto sp2 = sp1; // 複製 → 參考計數 +1
std::cout << "複製後 use_count: " << sp1.use_count() << "\n";
auto sp3 = sp1; // 再複製 → 參考計數 +1
std::cout << "再複製後 use_count: " << sp1.use_count() << "\n";
sp2.reset(); // sp2 放棄 → 參考計數 -1
std::cout << "sp2.reset() 後 use_count: " << sp1.use_count() << "\n";
} // sp3 離開作用域 → 參考計數 -1
std::cout << "sp3 離開作用域後 use_count: " << sp1.use_count() << "\n";
} // sp1 離開 → 參考計數歸零 → Resource 解構
std::cout << "(use_count 歸零,Resource 自動釋放)\n";
// ──────────────────────────────────────────
// 3. shared_ptr 的各種操作
// ──────────────────────────────────────────
section("3. shared_ptr 操作");
{
auto sp = std::make_shared<Resource>("Gamma");
// 解參考
std::cout << "sp->name(): " << sp->name() << "\n";
// get() — 取得原始指標
Resource* raw = sp.get();
std::cout << "raw->name(): " << raw->name() << "\n";
// bool 轉換
if (sp) {
std::cout << "sp 不為空\n";
}
// unique() — 是否只有一個擁有者(C++17 已棄用,C++20 移除)
std::cout << "use_count == 1: " << (sp.use_count() == 1 ? "是" : "否") << "\n";
// reset
sp.reset();
std::cout << "reset() 後 sp 為空: " << (!sp ? "是" : "否") << "\n";
}
// ──────────────────────────────────────────
// 4. shared_ptr 與自訂刪除器
// ──────────────────────────────────────────
section("4. shared_ptr 自訂刪除器");
{
// 與 unique_ptr 不同,shared_ptr 的刪除器不是型別的一部分
auto deleter = [](int* p) {
std::cout << " 自訂刪除器:釋放 *p = " << *p << "\n";
delete p;
};
std::shared_ptr<int> sp(new int(777), deleter);
std::cout << "值:" << *sp << "\n";
// 可以和其他 shared_ptr<int> 混用
std::shared_ptr<int> sp2 = sp;
std::cout << "use_count: " << sp.use_count() << "\n";
sp.reset();
std::cout << "sp.reset() 後 use_count (sp2): " << sp2.use_count() << "\n";
}
// ──────────────────────────────────────────
// 5. shared_ptr 在容器中
// ──────────────────────────────────────────
section("5. shared_ptr 在容器中");
{
std::vector<std::shared_ptr<Resource>> resources;
auto r1 = std::make_shared<Resource>("Res-A");
auto r2 = std::make_shared<Resource>("Res-B");
resources.push_back(r1);
resources.push_back(r2);
resources.push_back(r1); // r1 被多次共享
std::cout << "\n容器內容:\n";
for (const auto& r : resources) {
std::cout << " " << r->name()
<< " (use_count: " << r.use_count() << ")\n";
}
std::cout << "\nr1 的 use_count: " << r1.use_count() << "\n";
std::cout << "r2 的 use_count: " << r2.use_count() << "\n";
}
// ──────────────────────────────────────────
// 6. weak_ptr 基礎
// ──────────────────────────────────────────
section("6. weak_ptr 基礎");
{
std::shared_ptr<int> sp = std::make_shared<int>(42);
std::weak_ptr<int> wp = sp;
std::cout << "sp use_count: " << sp.use_count() << "\n";
std::cout << "wp.expired(): " << (wp.expired() ? "是" : "否") << "\n";
// lock() — 取得 shared_ptr
if (auto locked = wp.lock()) {
std::cout << "lock() 成功:*locked = " << *locked << "\n";
std::cout << "lock 後 use_count: " << sp.use_count() << "\n";
}
// locked 離開作用域,use_count 恢復
std::cout << "locked 銷毀後 use_count: " << sp.use_count() << "\n";
// 釋放 shared_ptr 後
sp.reset();
std::cout << "\nsp.reset() 後:\n";
std::cout << "wp.expired(): " << (wp.expired() ? "是" : "否") << "\n";
if (auto locked = wp.lock()) {
std::cout << "lock() 成功\n"; // 不會執行
} else {
std::cout << "lock() 失敗 — 物件已釋放\n";
}
}
// ──────────────────────────────────────────
// 7. 循環參考問題(shared_ptr 互相持有)
// ──────────────────────────────────────────
section("7. 循環參考問題(BadNode)");
{
std::cout << "建立兩個 BadNode 並互相連結:\n";
auto node1 = std::make_shared<BadNode>("A");
auto node2 = std::make_shared<BadNode>("B");
node1->next = node2; // A → B
node2->prev = node1; // B → A(循環!)
std::cout << "node1 use_count: " << node1.use_count() << "\n";
std::cout << "node2 use_count: " << node2.use_count() << "\n";
}
std::cout << "→ 離開作用域後 BadNode 的解構子「沒有」被呼叫 → 記憶體洩漏!\n";
// node1 的 use_count 從 2 降到 1(node2->prev 仍持有)
// node2 的 use_count 從 2 降到 1(node1->next 仍持有)
// 兩者都無法歸零 → 永遠不會釋放
// ──────────────────────────────────────────
// 8. 用 weak_ptr 打破循環參考(GoodNode)
// ──────────────────────────────────────────
section("8. weak_ptr 打破循環(GoodNode)");
{
std::cout << "建立兩個 GoodNode 並連結(prev 用 weak_ptr):\n";
auto node1 = std::make_shared<GoodNode>("X");
auto node2 = std::make_shared<GoodNode>("Y");
node1->next = node2; // X → Y(shared_ptr)
node2->prev = node1; // Y → X(weak_ptr,不增加計數)
std::cout << "node1 use_count: " << node1.use_count() << "\n";
std::cout << "node2 use_count: " << node2.use_count() << "\n";
// 透過 weak_ptr 存取
if (auto prev = node2->prev.lock()) {
std::cout << "node2 的前一個節點:" << prev->name << "\n";
}
}
std::cout << "→ GoodNode 的解構子正確被呼叫 ✓\n";
// ──────────────────────────────────────────
// 9. 三節點鏈結串列示範
// ──────────────────────────────────────────
section("9. 三節點鏈結串列");
{
auto n1 = std::make_shared<GoodNode>("Head");
auto n2 = std::make_shared<GoodNode>("Middle");
auto n3 = std::make_shared<GoodNode>("Tail");
// 建立雙向連結
n1->next = n2;
n2->prev = n1;
n2->next = n3;
n3->prev = n2;
// 正向走訪
std::cout << "正向走訪:";
for (auto current = n1; current; current = current->next) {
std::cout << current->name;
if (current->next) std::cout << " → ";
}
std::cout << "\n";
// 反向走訪(從 n3 開始)
std::cout << "反向走訪:";
auto current = n3;
while (current) {
std::cout << current->name;
if (auto prev = current->prev.lock()) {
std::cout << " → ";
current = prev;
} else {
break;
}
}
std::cout << "\n";
std::cout << "n1 use_count: " << n1.use_count()
<< ", n2 use_count: " << n2.use_count()
<< ", n3 use_count: " << n3.use_count() << "\n";
}
// ──────────────────────────────────────────
// 10. 觀察者模式中的 weak_ptr
// ──────────────────────────────────────────
section("10. 觀察者模式與 weak_ptr");
{
Observer obs1("Watcher-1");
Observer obs2("Watcher-2");
{
auto subject = std::make_shared<Subject>("重要事件");
obs1.watch(subject);
obs2.watch(subject);
std::cout << "Subject 存在時:\n";
obs1.report();
obs2.report();
}
std::cout << "\nSubject 已釋放後:\n";
obs1.report();
obs2.report();
}
// ──────────────────────────────────────────
// 11. make_shared vs new 的差異
// ──────────────────────────────────────────
section("11. make_shared vs new");
{
std::cout << "make_shared 的優點:\n";
std::cout << " 1. 只做一次記憶體配置(物件 + 控制區塊合併)\n";
std::cout << " 2. 例外安全(不會有部分建構的問題)\n";
std::cout << " 3. 更好的快取局部性\n";
std::cout << "\nmake_shared 的限制:\n";
std::cout << " 1. 無法使用自訂刪除器\n";
std::cout << " 2. 即使所有 shared_ptr 都釋放了,\n";
std::cout << " 若仍有 weak_ptr 存在,記憶體不會完全釋放\n";
std::cout << " (因為控制區塊和物件在同一塊記憶體中)\n";
std::cout << "\n概念圖:\n";
std::cout << " make_shared: [控制區塊 + 物件] ← 一次配置\n";
std::cout << " shared_ptr(new): [控制區塊] [物件] ← 兩次配置\n";
}
// ──────────────────────────────────────────
// 12. 智慧指標選擇總結
// ──────────────────────────────────────────
section("12. 智慧指標選擇總結");
{
std::cout << "┌──────────────┬──────────────────────────────┐\n";
std::cout << "│ 場景 │ 建議 │\n";
std::cout << "├──────────────┼──────────────────────────────┤\n";
std::cout << "│ 獨佔擁有 │ unique_ptr(預設選擇) │\n";
std::cout << "│ 共享擁有 │ shared_ptr │\n";
std::cout << "│ 觀察不擁有 │ weak_ptr + shared_ptr │\n";
std::cout << "│ 效能優先 │ unique_ptr(零額外開銷) │\n";
std::cout << "│ 打破循環 │ weak_ptr │\n";
std::cout << "└──────────────┴──────────────────────────────┘\n";
}
std::cout << "\n===== shared_ptr / weak_ptr 示範結束 =====\n";
return 0;
}
Artículos relacionados
C++
c
Actualizado 2026-07-21
deviceAlpha.h
deviceAlpha.h — c source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/deviceAlpha.h).
Leer artículo →
C++
c
Actualizado 2026-07-21
finalproject.c
finalproject.c — c source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/finalproject.c).
Leer artículo →
C++
cpp
Actualizado 2026-07-21
finalproject.cpp
finalproject.cpp — cpp source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/finalproject.cpp).
Leer artículo →
C++
c
Actualizado 2026-07-21
deviceAlpha.h
deviceAlpha.h — c source code from the C++ learning materials (C++/Mavis_Homework/Lab8/deviceAlpha.h).
Leer artículo →
C++
c
Actualizado 2026-07-21
lab8.c
lab8.c — c source code from the C++ learning materials (C++/Mavis_Homework/Lab8/lab8.c).
Leer artículo →
C++
cpp
Actualizado 2026-07-21
lab8.cpp
lab8.cpp — cpp source code from the C++ learning materials (C++/Mavis_Homework/Lab8/lab8.cpp).
Leer artículo →