首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++11 weak_ptr 詳解:弱引用機制、循環引用解決與實戰代碼

     編輯:關於C++

C++11 智能指針家族中,std::weak_ptr 是一種特殊的非擁有式觀察者。它不增加引用計數,不參與對象生命週期管理,專門用於解決 shared_ptr 循環引用導致的內存洩漏,以及實現緩存、觀察者模式等場景。

核心特性

  • 弱引用:指向 shared_ptr 管理的對象,構造/拷貝/析構不修改引用計數
  • 不可直接解引用:無 operator*、operator->,必須調用 lock() 獲取 shared_ptr 才能訪問對象
  • 臨時所有權:lock() 成功時返回新 shared_ptr,延長對象壽命至該 shared_ptr 銷毀
  • 過期檢測:expired() 判斷對象是否已銷毀
  • 打破循環引用:將循環鏈中一環改為 weak_ptr,讓引用計數歸零

典型應用場景

  1. 父子對象雙向引用:父 shared_ptr 子,子 weak_ptr 父
  2. 雙向鏈表/樹結構:父節點 shared_ptr 子,子節點 weak_ptr 父
  3. 緩存/觀察者:緩存持有 weak_ptr,對象銷毀自動失效
  4. 異步回調:回調持有 weak_ptr,執行前 lock() 檢查對象存活

關鍵成員函數

函數說明
lock()嘗試獲取共享所有權,成功返回 shared_ptr,失敗返回空
expired()等價於 use_count() == 0,檢查對象是否已銷毀
use_count()返回關聯 shared_ptr 的引用計數(僅供調試)
reset()釋放觀察關係,置空
swap()交換兩個 weak_ptr

實戰代碼示例集

1. 基礎觀察與過期檢測

std::weak_ptr<int> gw;

void f() {
    if (auto spt = gw.lock()) {
        std::cout << *spt << "\n";
    } else {
        std::cout << "gw is expired\n";
    }
}

int test_weak_ptr1() {
    {
        auto sp = std::make_shared<int>(42);
        gw = sp;
        f(); // 輸出 42
    } // sp 銷毀,對象釋放
    f(); // 輸出 "gw is expired"
    return 0;
}

2. 替代裸指針避免懸空指針

int test_weak_ptr2() {
    std::shared_ptr<int> sptr;
    sptr.reset(new int);
    *sptr = 10;
    
    std::weak_ptr<int> weak1 = sptr;
    sptr.reset(new int); // 原對象銷毀
    *sptr = 5;
    std::weak_ptr<int> weak2 = sptr;
    
    if (auto tmp = weak1.lock()) std::cout << *tmp << '\n';
    else std::cout << "weak1 is expired\n"; // 輸出 expired
    
    if (auto tmp = weak2.lock()) std::cout << *tmp << '\n'; // 輸出 5
    else std::cout << "weak2 is expired\n";
    return 0;
}

3. 觀察者模式:多對象相互觀察

class Controller {
public:
    int Num;
    std::string Status;
    std::vector<std::weak_ptr<Controller>> others;
    
    explicit Controller(int i) : Num(i), Status("On") {
        std::cout << "Creating Controller" << Num << std::endl;
    }
    ~Controller() { std::cout << "Destroying Controller" << Num << std::endl; }
    
    void CheckStatuses() const {
        for (auto& wp : others) {
            if (auto p = wp.lock()) {
                std::cout << "Status of " << p->Num << " = " << p->Status << std::endl;
            } else {
                std::cout << "Null object" << std::endl;
            }
        }
    }
};

void RunTest() {
    std::vector<std::shared_ptr<Controller>> v;
    for (int i = 0; i < 5; ++i) v.push_back(std::make_shared<Controller>(i));
    
    // 每個控制器觀察其他所有
    for (int i = 0; i < v.size(); ++i) {
        for (auto& p : v) {
            if (p->Num != i) {
                v[i]->others.push_back(p);
            }
        }
    }
    
    for (auto& p : v) {
        std::cout << "use_count = " << p.use_count() << std::endl;
        p->CheckStatuses();
    }
}

4. 父子對象打破循環引用

struct Child;
struct Parent {
    std::shared_ptr<Child> child;
    ~Parent() { std::cout << "Bye Parent" << std::endl; }
    void hi() const { std::cout << "Hello" << std::endl; }
};
struct Child {
    std::weak_ptr<Parent> parent; // 關鍵:用 weak_ptr
    ~Child() { std::cout << "Bye Child" << std::endl; }
};

int test_weak_ptr4() {
    auto parent = std::make_shared<Parent>();
    auto child = std::make_shared<Child>();
    parent->child = child;
    child->parent = parent;
    child->parent.lock()->hi(); // 通過 lock 訪問父對象
    return 0;
}
// 輸出:Hi → Bye Child → Bye Parent(正常銷毀)

5. 二叉樹父節點弱引用

class Node {
    int value;
public:
    std::shared_ptr<Node> leftPtr;
    std::shared_ptr<Node> rightPtr;
    std::weak_ptr<Node> parentPtr; // 父節點用 weak_ptr
    
    Node(int val) : value(val) { std::cout << "Constructor\n"; }
    ~Node() { std::cout << "Destructor\n"; }
};

int test_weak_ptr5() {
    auto ptr = std::make_shared<Node>(4);
    ptr->leftPtr = std::make_shared<Node>(2);
    ptr->leftPtr->parentPtr = ptr;
    ptr->rightPtr = std::make_shared<Node>(5);
    ptr->rightPtr->parentPtr = ptr;
    
    std::cout << "ptr use_count = " << ptr.use_count() << std::endl; // 1
    std::cout << "left use_count = " << ptr->leftPtr.use_count() << std::endl; // 1
    std::cout << "right use_count = " << ptr->rightPtr.use_count() << std::endl; // 1
    std::cout << "parentPtr lock count = " << ptr->rightPtr->parentPtr.lock().use_count() << std::endl; // 2
    return 0;
}

總結

  • weak_ptr 是 shared_ptr 的完美補充,解決「需訪問但不擁有」的場景
  • 永遠記住:先 lock(),再使用,lock() 返回的 shared_ptr 保證訪問期間對象存活
  • 循環引用檢查清單:父子、雙向鏈表、樹、圖、緩存、回調
  • 性能提示:use_count() 僅供調試,熱路徑避免頻繁調用

GitHub 完整代碼:https://github.com/fengbingchun/Messy_Test

weak_ptr 支持的操作表

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved