C++中智能指針weak_ptr的原理及使用
1. weak_ptr 的基本概念
weak_ptr 是 C++11 引入的一種智能指針,它與 shared_ptr 配合使用,主要解決以下問題:
- 打破循環(huán)引用:防止
shared_ptr之間的循環(huán)引用導(dǎo)致內(nèi)存泄漏 - 安全觀察:允許觀察共享對象而不影響其生命周期
- 避免懸空指針:可以檢測被觀察對象是否已被釋放
2. weak_ptr 的核心原理
2.1 控制塊結(jié)構(gòu)
weak_ptr 與 shared_ptr 共享同一個控制塊,控制塊包含:
struct ControlBlock {
std::atomic<size_t> shared_count; // 強引用計數(shù)
std::atomic<size_t> weak_count; // 弱引用計數(shù)
// 其他元數(shù)據(jù)(刪除器、分配器等)
};
2.2 內(nèi)存管理規(guī)則
- 對象銷毀條件:當(dāng)
shared_count歸零時,管理對象被銷毀 - 控制塊銷毀條件:當(dāng)
shared_count和weak_count都?xì)w零時,控制塊被釋放 - weak_ptr 不參與所有權(quán):僅增加
weak_count,不影響shared_count
2.3 工作流程
// 創(chuàng)建 shared_ptr(控制塊:shared=1, weak=0) auto sp = std::make_shared<int>(42); // 創(chuàng)建 weak_ptr(控制塊:shared=1, weak=1) std::weak_ptr<int> wp = sp; // shared_ptr 析構(gòu)(控制塊:shared=0, weak=1) sp.reset(); // 此時: // - 管理的 int 對象已被銷毀 // - 控制塊仍然存在(weak_count=1) // - wp.expired() == true // weak_ptr 析構(gòu)(控制塊:shared=0, weak=0) // 控制塊被釋放
3. weak_ptr 的關(guān)鍵操作
3.1 創(chuàng)建與賦值
// 從 shared_ptr 創(chuàng)建 auto sp = std::make_shared<MyClass>(); std::weak_ptr<MyClass> wp1(sp); // 拷貝構(gòu)造 std::weak_ptr<MyClass> wp2(wp1); // 賦值操作 std::weak_ptr<MyClass> wp3; wp3 = wp1;
3.2 檢查與升級
// 檢查對象是否有效
if (!wp.expired()) {
// 嘗試升級為 shared_ptr
if (auto sp = wp.lock()) {
// 使用 sp 安全訪問對象
}
}
3.3 資源釋放監(jiān)控
// 監(jiān)控資源釋放
std::weak_ptr<MyClass> wp;
{
auto sp = std::make_shared<MyClass>();
wp = sp;
// 對象存在
}
// 此時 wp.expired() == true
4. weak_ptr 的線程安全性
4.1 官方標(biāo)準(zhǔn)規(guī)定
- 控制塊操作是原子的:
weak_count的增減是線程安全的 - lock() 操作是線程安全的:升級為
shared_ptr的過程是原子的 - 對象訪問需要同步:通過
lock()獲取的shared_ptr需要額外同步
4.2 線程安全示例
std::shared_ptr<int> sp = std::make_shared<int>(42);
std::weak_ptr<int> wp(sp);
// 線程1
if (auto local_sp = wp.lock()) {
std::lock_guard<std::mutex> lock(mtx);
*local_sp = 10;
}
// 線程2
if (auto local_sp = wp.lock()) {
std::lock_guard<std::mutex> lock(mtx);
int val = *local_sp;
}
5. weak_ptr 的典型應(yīng)用場景
5.1 打破循環(huán)引用
class Parent {
std::shared_ptr<Child> child;
};
class Child {
std::weak_ptr<Parent> parent; // 使用 weak_ptr 避免循環(huán)
};
5.2 緩存系統(tǒng)
class Cache {
std::unordered_map<Key, std::weak_ptr<Resource>> cache;
std::shared_ptr<Resource> get(Key key) {
if (auto it = cache.find(key); it != cache.end()) {
if (auto sp = it->second.lock()) {
return sp; // 緩存命中
}
cache.erase(it); // 清理過期緩存
}
// 緩存未命中,創(chuàng)建新資源
auto sp = std::make_shared<Resource>(key);
cache[key] = sp;
return sp;
}
};
5.3 觀察者模式
class Subject {
std::vector<std::weak_ptr<Observer>> observers;
void notify() {
for (auto it = observers.begin(); it != observers.end(); ) {
if (auto obs = it->lock()) {
obs->update();
++it;
} else {
it = observers.erase(it); // 移除無效觀察者
}
}
}
};
6. weak_ptr 的實現(xiàn)細(xì)節(jié)
6.1 控制塊生命周期
6.2 lock() 的原子實現(xiàn)
lock() 操作必須保證線程安全,偽代碼實現(xiàn):
shared_ptr<T> lock() const noexcept {
ControlBlock* cb = get_control_block();
size_t sc = cb->shared_count.load();
do {
if (sc == 0) return nullptr; // 對象已釋放
// 嘗試增加 shared_count(CAS操作)
} while (!cb->shared_count.compare_exchange_weak(sc, sc + 1));
return shared_ptr<T>(cb); // 創(chuàng)建新的 shared_ptr
}
7. weak_ptr 的注意事項
不能直接解引用:必須先用 lock() 升級為 shared_ptr
// 錯誤用法
// *wp; // 編譯錯誤
// 正確用法
if (auto sp = wp.lock()) {
*sp = value;
}
性能考慮:
lock()操作包含原子操作,有一定開銷- 控制塊需要額外內(nèi)存(通常16-32字節(jié))
構(gòu)造函數(shù)限制:
// 不能直接從裸指針構(gòu)造 // std::weak_ptr<int> wp(new int(42)); // 錯誤 // 必須從 shared_ptr 構(gòu)造 auto sp = std::make_shared<int>(42); std::weak_ptr<int> wp(sp); // 正確
8. 性能優(yōu)化建議
避免頻繁 lock/unlock:在需要時緩存 shared_ptr
void process(std::weak_ptr<Data> wp) {
auto sp = wp.lock(); // 只調(diào)用一次 lock
if (!sp) return;
// 多次使用 sp 而不重復(fù)調(diào)用 lock
sp->operation1();
sp->operation2();
}
及時清理失效 weak_ptr:定期檢查并移除 expired() 的 weak_ptr
考慮使用 make_shared:對象和控制塊單次分配,減少內(nèi)存碎片
9. 與其它智能指針的對比
| 特性 | weak_ptr | shared_ptr | unique_ptr |
|---|---|---|---|
| 所有權(quán) | 無 | 共享 | 獨占 |
| 影響生命周期 | 否 | 是 | 是 |
| 直接訪問對象 | 需通過 lock() | 可直接訪問 | 可直接訪問 |
| 引用計數(shù) | 只增加 weak_count | 增加 shared_count | 無 |
| 典型用途 | 打破循環(huán)引用/觀察 | 共享所有權(quán) | 獨占所有權(quán) |
10. 現(xiàn)代 C++ 中的增強
10.1 C++17 的 weak_from_this
class MyClass : public std::enable_shared_from_this<MyClass> {
public:
std::weak_ptr<MyClass> get_weak() {
return weak_from_this(); // 安全獲取 weak_ptr
}
};
auto obj = std::make_shared<MyClass>();
auto wobj = obj->get_weak(); // 安全獲取 weak_ptr
10.2 C++20 的原子 shared_ptr/weak_ptr
std::atomic<std::weak_ptr<int>> atomic_wp; atomic_wp.store(wp, std::memory_order_release); auto current = atomic_wp.load(std::memory_order_acquire);
11. 總結(jié)
weak_ptr 的核心原理可以總結(jié)為:
- 不參與所有權(quán):僅觀察不擁有,不影響對象生命周期
- 共享控制塊:與
shared_ptr共享同一控制塊,維護weak_count - 安全升級:通過
lock()原子操作獲取可用的shared_ptr - 自動清理:當(dāng)最后一個
weak_ptr析構(gòu)后,控制塊被釋放
正確使用 weak_ptr 可以:
- 有效解決循環(huán)引用導(dǎo)致的內(nèi)存泄漏
- 實現(xiàn)安全的對象觀察模式
- 構(gòu)建高效的緩存系統(tǒng)
- 開發(fā)更健壯的異步代碼
理解 weak_ptr 的工作原理對于設(shè)計復(fù)雜的內(nèi)存管理系統(tǒng)和避免資源泄漏至關(guān)重要,它是現(xiàn)代 C++ 高效內(nèi)存管理工具鏈中不可或缺的一環(huán)。
到此這篇關(guān)于C++中智能指針weak_ptr的原理及使用的文章就介紹到這了,更多相關(guān)C++ weak_ptr內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
結(jié)合C++11的新特性來解析C++中的枚舉與聯(lián)合
這篇文章主要介紹了C++編程中的枚舉與聯(lián)合,結(jié)合了范圍(或強類型)enum class類型等C++11的新特性來講解,需要的朋友可以參考下2016-01-01
C語言枚舉(enum)和聯(lián)合(union)實例分享
在本篇文章里小編給大家整理了關(guān)于C語言枚舉(enum)和聯(lián)合(union)實例內(nèi)容,需要的朋友們可以學(xué)習(xí)下。2020-03-03
vc中float與DWORD的互想轉(zhuǎn)換實現(xiàn)代碼
這篇文章主要介紹了vc中float與DWORD的互想轉(zhuǎn)換實現(xiàn)代碼,需要的朋友可以參考下2017-06-06

