目錄
一、循環引用(最常見場景)
示例代碼
內存泄漏原因
示例代碼
內存泄漏(或雙重釋放)原因
三、解決方案
1. 循環引用:使用?std::weak_ptr
四、其他潛在風險
2. 數組管理
總結
std::shared_ptr
?是 C++ 中用于管理動態內存的智能指針,通過引用計數機制自動釋放對象。但在某些場景下,它仍可能導致內存泄漏。以下通過具體例子說明:
一、循環引用(最常見場景)
示例代碼
#include <iostream>
#include <memory>class B; // 前向聲明class A {
public:std::shared_ptr<B> b_ptr;~A() { std::cout << "A destroyed" << std::endl; }
};class B {
public:std::shared_ptr<A> a_ptr;~B() { std::cout << "B destroyed" << std::endl; }
};int main() {auto a = std::make_shared<A>(); // a的引用計數為1auto b = std::make_shared<B>(); // b的引用計數為1a->b_ptr = b; // b的引用計數變為2b->a_ptr = a; // a的引用計數變為2// main函數結束時:// a和b的引用計數減1,但仍為1(互相持有對方的shared_ptr)// 導致兩者的析構函數都不會被調用,內存泄漏!return 0;
}
內存泄漏原因
a
?和?b
?互相持有對方的?shared_ptr
,形成循環引用。- 當?
main
?函數結束時,a
?和?b
?的局部變量被銷毀,引用計數減 1,但仍為 1(因為對方的成員變量還持有自己的指針)。 - 引用計數永遠無法降為 0,導致析構函數不會被調用,內存無法釋放。
二、共享指針管理的對象包含自身的?shared_ptr
示例代碼
#include <iostream>
#include <memory>class Bad {
public:std::shared_ptr<Bad> getSelf() {return std::shared_ptr<Bad>(this); // 危險!}~Bad() { std::cout << "Bad destroyed" << std::endl; }
};int main() {auto b1 = std::make_shared<Bad>();auto b2 = b1->getSelf(); // 創建了第二個獨立的shared_ptr管理同一對象// b1和b2的引用計數均為1// main函數結束時,b1和b2分別析構,導致對象被delete兩次(雙重釋放)return 0;
}
內存泄漏(或雙重釋放)原因
getSelf()
?方法中使用?this
?指針創建了一個新的?shared_ptr
,與原?shared_ptr
?無關。- 同一對象被兩個獨立的?
shared_ptr
?管理,引用計數各自為 1。 - 當兩個?
shared_ptr
?析構時,對象被重復釋放,導致未定義行為(通常是程序崩潰)。
三、解決方案
1. 循環引用:使用?std::weak_ptr
std::weak_ptr
?是一種弱引用,不增加引用計數,用于打破循環:
class A {
public:std::weak_ptr<B> b_ptr; // 改為weak_ptr~A() { std::cout << "A destroyed" << std::endl; }
};class B {
public:std::weak_ptr<A> a_ptr; // 改為weak_ptr~B() { std::cout << "B destroyed" << std::endl; }
};
weak_ptr
?不會增加引用計數,當?a
?和?b
?局部變量銷毀時,引用計數降為 0,對象正常釋放。
2. 對象獲取自身的?shared_ptr
:繼承?std::enable_shared_from_this
#include <memory>class Good : public std::enable_shared_from_this<Good> {
public:std::shared_ptr<Good> getSelf() {return shared_from_this(); // 安全!}~Good() { std::cout << "Good destroyed" << std::endl; }
};
shared_from_this()
?返回一個與已有?shared_ptr
?共享引用計數的新指針,避免雙重釋放。
四、其他潛在風險
1. 混合使用原始指針和?shared_ptr
int* raw = new int(42);
std::shared_ptr<int> ptr1(raw);
std::shared_ptr<int> ptr2(raw); // 錯誤!兩個獨立的shared_ptr管理同一內存
- 同一原始指針被多個?
shared_ptr
?獨立管理,導致雙重釋放。
2. 數組管理
std::shared_ptr
?默認使用?delete
?釋放對象,若管理數組需自定義刪除器:
std::shared_ptr<int[]> arr(new int[10]); // 錯誤!默認使用delete而非delete[]
std::shared_ptr<int> arr(new int[10], [](int* p) { delete[] p; }); // 正確
總結
std::shared_ptr
?內存泄漏的核心原因是引用計數無法降為 0,常見于循環引用和錯誤的指針管理。使用?std::weak_ptr
?和?std::enable_shared_from_this
?可有效避免這些問題。在實際開發中,應盡量避免手動管理原始指針,確保所有動態內存都由智能指針統一管理。