C++各種智能指針的使用小結(jié)
C++智能指針是RAII(資源獲取即初始化)思想的核心實踐,用于自動管理動態(tài)內(nèi)存,解決傳統(tǒng)裸指針的內(nèi)存泄漏、懸垂指針、重復(fù)釋放等問題。
C++11及后續(xù)標(biāo)準(zhǔn)提供了多種智能指針,核心區(qū)別在于所有權(quán)模型(獨占/共享/弱引用),定義在<memory> 頭文件中。
一、智能指針的分類與核心特性
C++標(biāo)準(zhǔn)庫中的智能指針主要有4種(auto_ptr已被廢棄):
類型 | 所有權(quán)模型 | 核心特點 |
|---|---|---|
auto_ptr | 獨占(已廢棄) | 拷貝時轉(zhuǎn)移所有權(quán),易導(dǎo)致意外失效,禁止使用? |
unique_ptr | 獨占所有權(quán) | 同一時間僅一個指針擁有對象,不可拷貝,僅可移動? |
shared_ptr | 共享所有權(quán) | 多個指針共享對象,通過引用計數(shù)跟蹤所有者數(shù)量,計數(shù)歸零時釋放對象 |
weak_ptr | 弱引用(無所有權(quán)) | 指向shared_ptr管理的對象,但不增加引用計數(shù),用于解決循環(huán)引用 |
二、各智能指針詳解
1.unique_ptr:獨占所有權(quán)的輕量級指針
unique_ptr是最常用的智能指針之一,適用于單一所有者的場景(如動態(tài)數(shù)組、局部資源管理)。
核心特性:
- 獨占性:不能拷貝(
copy constructor/copy assignment被刪除),只能通過std::move轉(zhuǎn)移所有權(quán)。 - 輕量:大小與裸指針相同(無額外開銷,除非自定義刪除器)。
- 支持?jǐn)?shù)組:
unique_ptr<T[]>重載了operator[],自動用delete[]釋放。 - 自定義刪除器:可指定任意資源釋放邏輯(如文件句柄、網(wǎng)絡(luò)連接)。
用法示例:
#include <memory>
#include <iostream>
int main() {
// 1. 創(chuàng)建:優(yōu)先用make_unique(C++14引入,更安全高效)
std::unique_ptr<int> p1 = std::make_unique<int>(42);
// 等價于:std::unique_ptr<int> p1(new int(42));
// 2. 訪問對象:用*或->
std::cout << *p1 << std::endl; // 輸出42
// 3. 移動所有權(quán)(不可拷貝)
std::unique_ptr<int> p2 = std::move(p1); // p1失去所有權(quán),變?yōu)閚ullptr
if (!p1) std::cout << "p1 is null" << std::endl;
// 4. 數(shù)組版本
std::unique_ptr<int[]> arr = std::make_unique<int[]>(3);
arr[0] = 1; arr[1] = 2; arr[2] = 3;
// 5. 釋放所有權(quán):release()返回裸指針,不再管理
int* raw = p2.release();
delete raw; // 需手動釋放(不推薦,盡量用智能指針全程管理)
// 6. 重置:reset()釋放當(dāng)前對象,可選新對象
p2.reset(new int(100)); // 先釋放原對象(若有),再管理新對象
p2.reset(); // 釋放對象,變?yōu)閚ullptr
return 0;
}自定義刪除器示例(管理文件句柄):
#include <cstdio>
#include <memory>
// 自定義刪除器:關(guān)閉文件
struct FileDeleter {
void operator()(FILE* fp) const {
if (fp) std::fclose(fp);
}
};
int main() {
// 用自定義刪除器創(chuàng)建unique_ptr
std::unique_ptr<FILE, FileDeleter> fp(std::fopen("test.txt", "w"));
if (fp) std::fputs("hello", fp.get()); // get()返回裸指針
// 離開作用域時,自動調(diào)用FileDeleter釋放文件
return 0;
}2.shared_ptr:共享所有權(quán)的引用計數(shù)指針
shared_ptr適用于多所有者的場景(如容器中存儲動態(tài)對象、跨模塊共享資源)。
核心特性:
- 共享性:多個
shared_ptr可指向同一對象,通過引用計數(shù)跟蹤所有者數(shù)量。 - 引用計數(shù):每次拷貝
shared_ptr,計數(shù)+1;析構(gòu)或reset()時,計數(shù)-1;計數(shù)歸零時,釋放對象。 - 高效創(chuàng)建:優(yōu)先用
std::make_shared(C++11引入),一次性分配對象內(nèi)存和控制塊(含引用計數(shù)、弱引用計數(shù)),比直接new更高效。 - 線程安全:引用計數(shù)的增減是原子操作(多線程安全),但對象本身的訪問需額外同步(如互斥鎖)。
用法示例:
#include <memory>
#include <iostream>
int main() {
// 1. 創(chuàng)建:優(yōu)先用make_shared(更高效)
auto p1 = std::make_shared<int>(42);
std::cout << "use_count: " << p1.use_count() << std::endl; // 輸出1
// 2. 共享所有權(quán):拷貝時計數(shù)+1
auto p2 = p1;
std::cout << "use_count: " << p1.use_count() << std::endl; // 輸出2
// 3. 釋放所有權(quán):reset()減少計數(shù)
p1.reset();
std::cout << "use_count after p1.reset(): " << p2.use_count() << std::endl; // 輸出1
// 4. 自定義刪除器(管理動態(tài)數(shù)組)
auto arr = std::shared_ptr<int>(new int[3], [](int* p) { delete[] p; });
arr.get()[0] = 1; // 訪問數(shù)組元素
return 0;
}注意:make_sharedVS 直接new
make_shared的優(yōu)勢:
- 更安全:避免裸指針暴露(如
func(std::shared_ptr<int>(new int), func2())可能因編譯器優(yōu)化導(dǎo)致內(nèi)存泄漏); - 更高效:一次性分配對象和控制塊,減少內(nèi)存碎片;
- 異常安全:構(gòu)造過程中若拋出異常,
make_shared不會泄漏內(nèi)存。
例外情況:需要自定義刪除器或延遲初始化時,只能用new。
3.weak_ptr:弱引用指針(解決循環(huán)引用)
weak_ptr是shared_ptr的“觀察者”,不擁有對象所有權(quán),僅指向shared_ptr管理的對象,用于解決循環(huán)引用問題。
核心問題:循環(huán)引用
當(dāng)兩個shared_ptr互相指向?qū)Ψ綍r,引用計數(shù)永遠無法歸零,導(dǎo)致內(nèi)存泄漏:
#include <memory>
struct A { std::shared_ptr<B> b; };
struct B { std::shared_ptr<A> a; };
int main() {
auto a = std::make_shared<A>();
auto b = std::make_shared<B>();
a->b = b; // A的b指向B,B的計數(shù)+1(變?yōu)?)
b->a = a; // B的a指向A,A的計數(shù)+1(變?yōu)?)
// 離開作用域時,a和b的計數(shù)各減1(變?yōu)?),無法釋放!
return 0;
}weak_ptr的解決方案
將其中一個shared_ptr改為weak_ptr(弱引用,不增加計數(shù)):
struct A { std::weak_ptr<B> b; }; // 弱引用,不增加B的計數(shù)
struct B { std::weak_ptr<A> a; }; // 弱引用,不增加A的計數(shù)
int main() {
auto a = std::make_shared<A>();
auto b = std::make_shared<B>();
a->b = b; // B的計數(shù)仍為1(weak_ptr不增加)
b->a = a; // A的計數(shù)仍為1(weak_ptr不增加)
// 離開作用域時,a和b的計數(shù)各減1(變?yōu)?),正常釋放!
return 0;
}weak_ptr的用法:
- 創(chuàng)建:由
shared_ptr構(gòu)造,或通過lock()獲取shared_ptr。 - 訪問對象:必須用
lock()轉(zhuǎn)換為shared_ptr(若對象已釋放,返回空shared_ptr)。 - 檢查有效性:用
expired()判斷對象是否已釋放(use_count() == 0)。
示例:
#include <memory>
#include <iostream>
int main() {
auto sp = std::make_shared<int>(42);
std::weak_ptr<int> wp = sp; // 弱引用指向sp的對象
// 1. 檢查有效性
if (!wp.expired()) {
std::cout << "Object exists" << std::endl;
}
// 2. 訪問對象:lock()獲取shared_ptr
auto locked_sp = wp.lock();
if (locked_sp) {
std::cout << *locked_sp << std::endl; // 輸出42
}
// 3. 釋放原對象
sp.reset();
if (wp.expired()) {
std::cout << "Object has been released" << std::endl;
}
return 0;
}三、智能指針的關(guān)鍵注意事項
- 禁止用裸指針初始化多個智能指針:
- 會導(dǎo)致多個智能指針獨立管理同一對象,析構(gòu)時重復(fù)釋放。
- ? 錯誤:
int* p = new int; auto p1 = std::shared_ptr<int>(p); auto p2 = std::shared_ptr<int>(p); - ? 正確:
auto p1 = std::make_shared<int>(42); auto p2 = p1;
- 優(yōu)先用make_shared/make_unique:
- 避免裸指針暴露,提升安全性和效率(見前文)。
- 避免循環(huán)引用:
- 用
weak_ptr打破shared_ptr之間的雙向依賴(見前文)。
- 用
- 不要手動delete智能指針的get()結(jié)果:
get()返回的裸指針是智能指針內(nèi)部管理的,手動delete會導(dǎo)致雙重釋放。
- 線程安全:
shared_ptr的引用計數(shù)是線程安全的(原子操作);shared_ptr的對象本身不是線程安全的(多線程訪問需同步);unique_ptr不支持多線程(獨占性)。
四、智能指針的選擇指南
場景 | 推薦智能指針 |
|---|---|
單一所有者(如局部變量) | unique_ptr |
多所有者(如容器共享) | shared_ptr |
解決循環(huán)引用 | weak_ptr |
管理數(shù)組 | unique_ptr<T[]> |
管理非內(nèi)存資源(文件、socket) | 帶自定義刪除器的unique_ptr/shared_ptr |
五、總結(jié)
智能指針是C++現(xiàn)代編程的必備工具,核心是通過所有權(quán)模型自動管理內(nèi)存。記?。?/p>
- 獨占用
unique_ptr,共享用shared_ptr; - 循環(huán)引用用
weak_ptr破局; - 優(yōu)先用
make_shared/make_unique; - 避免裸指針與智能指針混用。
掌握智能指針能大幅減少內(nèi)存錯誤,提升代碼的健壯性和可維護性——這也是嵌入式Linux開發(fā)中(如驅(qū)動、Qt應(yīng)用、流媒體資源管理)的關(guān)鍵技能。
到此這篇關(guān)于C++各種智能指針的使用小結(jié)的文章就介紹到這了,更多相關(guān)C++各種智能指針內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
c語言實現(xiàn)把文件中數(shù)據(jù)讀取并存到數(shù)組中
下面小編就為大家?guī)硪黄猚語言實現(xiàn)把文件中數(shù)據(jù)讀取并存到數(shù)組中。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-12-12
C++17中的std::from_chars函數(shù)使用及說明
這篇文章主要介紹了C++17中的std::from_chars函數(shù)使用及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-10-10

