C++ scoped_ptr 和 unique_ptr對(duì)比分析
在 C++ 中,scoped_ptr 和 unique_ptr 都是用于管理獨(dú)占所有權(quán)的智能指針,但它們有一些重要的區(qū)別。
1. scoped_ptr
scoped_ptr 是 Boost 庫(kù)中的智能指針,提供了簡(jiǎn)單的獨(dú)占所有權(quán)語(yǔ)義。
基本特性
#include <boost/scoped_ptr.hpp>
class MyClass {
public:
MyClass() { std::cout << "MyClass constructed\n"; }
~MyClass() { std::cout << "MyClass destroyed\n"; }
void doSomething() { std::cout << "Doing something\n"; }
};
int main() {
boost::scoped_ptr<MyClass> ptr(new MyClass);
ptr->doSomething();
// 當(dāng) ptr 離開作用域時(shí),會(huì)自動(dòng)刪除管理的對(duì)象
return 0;
}主要特點(diǎn)
- 不可拷貝和移動(dòng):
scoped_ptr不能被復(fù)制或移動(dòng) - 簡(jiǎn)單輕量:幾乎沒有性能開銷
- 明確所有權(quán):清楚地表明指針擁有對(duì)象的所有權(quán)
boost::scoped_ptr<MyClass> ptr1(new MyClass); // boost::scoped_ptr<MyClass> ptr2 = ptr1; // 錯(cuò)誤!不能拷貝 // boost::scoped_ptr<MyClass> ptr3(ptr1); // 錯(cuò)誤!不能拷貝構(gòu)造
2. unique_ptr
unique_ptr 是 C++11 標(biāo)準(zhǔn)引入的智能指針,提供了更豐富的功能。
基本用法
#include <memory>
class MyClass {
public:
MyClass(int value) : data(value) {
std::cout << "MyClass constructed with " << value << "\n";
}
~MyClass() { std::cout << "MyClass destroyed\n"; }
void show() { std::cout << "Data: " << data << "\n"; }
private:
int data;
};
int main() {
// 創(chuàng)建 unique_ptr
std::unique_ptr<MyClass> ptr1(new MyClass(42));
// 使用 make_unique (C++14)
auto ptr2 = std::make_unique<MyClass>(100);
ptr1->show();
ptr2->show();
return 0; // 自動(dòng)釋放內(nèi)存
}3. 主要區(qū)別對(duì)比
| 特性 | scoped_ptr (Boost) | unique_ptr (C++11) |
|---|---|---|
| 標(biāo)準(zhǔn)支持 | 僅 Boost 庫(kù) | C++11 標(biāo)準(zhǔn)庫(kù) |
| 移動(dòng)語(yǔ)義 | ? 不支持 | ? 支持 |
| 數(shù)組支持 | ? 需要 scoped_array | ? 內(nèi)置支持 |
| 自定義刪除器 | ? 不支持 | ? 支持 |
| 容器兼容性 | ? 不能放入容器 | ? 可以放入容器 |
| 性能 | 極輕量 | 輕量,功能更多 |
4. unique_ptr 的高級(jí)特性
移動(dòng)語(yǔ)義
std::unique_ptr<MyClass> createObject() {
return std::make_unique<MyClass>(999);
}
int main() {
std::unique_ptr<MyClass> ptr1 = std::make_unique<MyClass>(42);
// 移動(dòng)所有權(quán)
std::unique_ptr<MyClass> ptr2 = std::move(ptr1);
if (!ptr1) {
std::cout << "ptr1 現(xiàn)在為空\(chéng)n";
}
if (ptr2) {
ptr2->show(); // 正常使用
}
// 從函數(shù)返回
auto ptr3 = createObject();
ptr3->show();
return 0;
}數(shù)組支持
// 管理動(dòng)態(tài)數(shù)組
std::unique_ptr<int[]> arrPtr(new int[10]);
for (int i = 0; i < 10; ++i) {
arrPtr[i] = i * i; // 可以直接使用下標(biāo)
}
// 或者使用 make_unique (C++14)
auto arrPtr2 = std::make_unique<int[]>(5);自定義刪除器
// 文件指針的自定義刪除器
struct FileDeleter {
void operator()(FILE* file) {
if (file) {
fclose(file);
std::cout << "File closed\n";
}
}
};
int main() {
std::unique_ptr<FILE, FileDeleter> filePtr(fopen("test.txt", "w"));
if (filePtr) {
fputs("Hello, World!", filePtr.get());
}
// 文件會(huì)自動(dòng)關(guān)閉
return 0;
}在容器中使用
#include <vector>
#include <memory>
int main() {
std::vector<std::unique_ptr<MyClass>> objects;
// 添加對(duì)象到向量
objects.push_back(std::make_unique<MyClass>(1));
objects.push_back(std::make_unique<MyClass>(2));
objects.push_back(std::make_unique<MyClass>(3));
// 使用對(duì)象
for (const auto& obj : objects) {
obj->show();
}
return 0;
}5. 所有權(quán)轉(zhuǎn)移模式
函數(shù)參數(shù)傳遞
void takeOwnership(std::unique_ptr<MyClass> ptr) {
std::cout << "函數(shù)獲得了對(duì)象的所有權(quán)\n";
ptr->show();
} // ptr 離開作用域,對(duì)象被銷毀
void borrowObject(MyClass* ptr) {
std::cout << "函數(shù)只是借用對(duì)象\n";
ptr->show();
} // 對(duì)象不會(huì)被銷毀
int main() {
auto ptr = std::make_unique<MyClass>(42);
// 轉(zhuǎn)移所有權(quán)
takeOwnership(std::move(ptr));
// 此時(shí) ptr 為空
if (!ptr) {
std::cout << "ptr 已為空\(chéng)n";
}
// 重新創(chuàng)建
ptr = std::make_unique<MyClass>(100);
// 只是借用,不轉(zhuǎn)移所有權(quán)
borrowObject(ptr.get());
// ptr 仍然有效
ptr->show();
return 0;
}6. 資源管理示例
對(duì)比原始指針
// 不好的做法 - 使用原始指針
void badExample() {
MyClass* rawPtr = new MyClass(42);
// ... 一些代碼 ...
if (someCondition) {
return; // 內(nèi)存泄漏!
}
// ... 更多代碼 ...
delete rawPtr; // 容易忘記
}
// 好的做法 - 使用 unique_ptr
void goodExample() {
auto ptr = std::make_unique<MyClass>(42);
// ... 一些代碼 ...
if (someCondition) {
return; // 自動(dòng)釋放內(nèi)存!
}
// ... 更多代碼 ...
// 不需要手動(dòng)刪除
}7. 實(shí)際應(yīng)用場(chǎng)景
工廠模式
class Product {
public:
virtual ~Product() = default;
virtual void use() = 0;
};
class ConcreteProduct : public Product {
public:
void use() override {
std::cout << "Using ConcreteProduct\n";
}
};
std::unique_ptr<Product> createProduct() {
return std::make_unique<ConcreteProduct>();
}
int main() {
auto product = createProduct();
product->use();
return 0;
}Pimpl 慣用法
// MyClass.h
class MyClass {
public:
MyClass();
~MyClass(); // 需要顯式定義,因?yàn)?unique_ptr 需要完整類型
void publicMethod();
private:
class Impl;
std::unique_ptr<Impl> pImpl;
};
// MyClass.cpp
class MyClass::Impl {
public:
void privateMethod() {
std::cout << "Private method called\n";
}
int data = 42;
};
MyClass::MyClass() : pImpl(std::make_unique<Impl>()) {}
MyClass::~MyClass() = default; // 需要看到 Impl 的完整定義
void MyClass::publicMethod() {
pImpl->privateMethod();
}總結(jié)
scoped_ptr:簡(jiǎn)單的獨(dú)占所有權(quán),適用于不需要移動(dòng)語(yǔ)義的簡(jiǎn)單場(chǎng)景unique_ptr:功能豐富的獨(dú)占所有權(quán)指針,是現(xiàn)代 C++ 的首選
推薦使用 unique_ptr,因?yàn)椋?/p>
- 它是 C++ 標(biāo)準(zhǔn)的一部分
- 支持移動(dòng)語(yǔ)義,更靈活
- 有更好的容器兼容性
- 支持自定義刪除器和數(shù)組
在現(xiàn)代 C++ 開發(fā)中,應(yīng)該優(yōu)先使用 unique_ptr 來(lái)管理獨(dú)占所有權(quán)的資源,避免使用原始指針和 scoped_ptr。
std::unique ptr<Entity> entity = std::make unique<Entity>();
代碼解析
std::unique_ptr<Entity> entity = std::make_unique<Entity>();
1.std::unique_ptr<Entity>
std::unique_ptr: 是一個(gè)智能指針類,提供獨(dú)占所有權(quán)的內(nèi)存管理<Entity>: 模板參數(shù),指定指針指向的類型為Entityentity: 變量名
2.std::make_unique<Entity>()
std::make_unique: C++14 引入的工廠函數(shù),用于創(chuàng)建unique_ptr<Entity>(): 在堆上動(dòng)態(tài)分配一個(gè)Entity對(duì)象,調(diào)用其默認(rèn)構(gòu)造函數(shù)
等效的傳統(tǒng)寫法
// 傳統(tǒng)方式(不推薦) std::unique_ptr<Entity> entity(new Entity()); // 或者 Entity* raw_ptr = new Entity(); std::unique_ptr<Entity> entity(raw_ptr);
優(yōu)勢(shì)
- 異常安全:
make_unique提供強(qiáng)異常安全保證 - 代碼簡(jiǎn)潔: 一行完成內(nèi)存分配和智能指針構(gòu)造
- 避免內(nèi)存泄漏: 自動(dòng)管理內(nèi)存生命周期
- 獨(dú)占所有權(quán): 防止多個(gè)指針指向同一對(duì)象
內(nèi)存管理
當(dāng) entity 離開作用域時(shí),會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù)并釋放內(nèi)存:
{
std::unique_ptr<Entity> entity = std::make_unique<Entity>();
// 使用 entity...
} // 此處 entity 自動(dòng)銷毀,Entity 對(duì)象被刪除
這種寫法是現(xiàn)代 C++ 中管理動(dòng)態(tài)內(nèi)存的推薦方式。
unique_ptr(const Myt&)= delete; Myt& operator=(const Myt&)= delete; // 這兩行代碼使用了 C++ 的 刪除函數(shù)(deleted functions) 特性來(lái)顯式禁止拷貝操作。
第一行:禁止拷貝構(gòu)造函數(shù)
unique_ptr(const Myt&) = delete;
unique_ptr(const Myt&): 拷貝構(gòu)造函數(shù)的聲明Myt通常是模板參數(shù),代表unique_ptr本身的類型- 參數(shù)
const Myt&表示接受一個(gè)常量引用到同類型的unique_ptr
= delete: 將該函數(shù)標(biāo)記為"已刪除"- 編譯時(shí)如果嘗試調(diào)用此函數(shù)會(huì)導(dǎo)致錯(cuò)誤
- 比將其聲明為
private更直接明確
第二行:禁止拷貝賦值運(yùn)算符
Myt& operator=(const Myt&) = delete;
Myt& operator=(const Myt&): 拷貝賦值運(yùn)算符的聲明- 返回
Myt&(引用到當(dāng)前對(duì)象),支持鏈?zhǔn)劫x值 - 參數(shù)
const Myt&表示接受一個(gè)常量引用到同類型的unique_ptr
- 返回
= delete: 同樣標(biāo)記為已刪除
設(shè)計(jì)意圖
為什么unique_ptr要禁止拷貝?
獨(dú)占所有權(quán)語(yǔ)義:
std::unique_ptr<Entity> ptr1 = std::make_unique<Entity>(); std::unique_ptr<Entity> ptr2 = ptr1; // 編譯錯(cuò)誤!
避免資源重復(fù)釋放:
- 如果允許拷貝,兩個(gè)
unique_ptr可能指向同一對(duì)象 - 析構(gòu)時(shí)會(huì)導(dǎo)致雙重釋放(double free)
- 如果允許拷貝,兩個(gè)
明確資源轉(zhuǎn)移:
- 使用
std::move()進(jìn)行所有權(quán)轉(zhuǎn)移
std::unique_ptr<Entity> ptr2 = std::move(ptr1); // 允許:轉(zhuǎn)移所有權(quán)
- 使用
對(duì)比其他智能指針
std::shared_ptr: 允許拷貝(引用計(jì)數(shù))std::weak_ptr: 允許拷貝(不增加引用計(jì)數(shù))std::unique_ptr: 禁止拷貝(獨(dú)占所有權(quán))
現(xiàn)代 C++ 最佳實(shí)踐
使用 = delete 比傳統(tǒng)的 private 方法更清晰:
// 傳統(tǒng)方法(C++98/03)
class MyClass {
private:
MyClass(const MyClass&); // 不實(shí)現(xiàn)
MyClass& operator=(const MyClass&); // 不實(shí)現(xiàn)
};
// 現(xiàn)代方法(C++11+)
class MyClass {
public:
MyClass(const MyClass&) = delete;
MyClass& operator=(const MyClass&) = delete;
};這種設(shè)計(jì)確保了 unique_ptr 的獨(dú)占所有權(quán)語(yǔ)義,防止意外的資源管理錯(cuò)誤。
??優(yōu)先選擇使用unique_ptr,其次是shared_ptr??
這種說(shuō)法源于 C++ 核心指南和現(xiàn)代 C++ 最佳實(shí)踐,主要有以下幾個(gè)重要原因:
1. 所有權(quán)語(yǔ)義更明確
unique_ptr- 獨(dú)占所有權(quán)
std::unique_ptr<Entity> createEntity() {
return std::make_unique<Entity>(); // 明確:所有權(quán)被轉(zhuǎn)移出去
}
auto entity = createEntity(); // 明確:我是唯一所有者shared_ptr- 共享所有權(quán)(可能模糊)
std::shared_ptr<Entity> createEntity() {
return std::make_shared<Entity>(); // 模糊:誰(shuí)擁有這個(gè)對(duì)象?
}
auto entity = createEntity(); // 可能有多個(gè)共享所有者2. 性能優(yōu)勢(shì)
內(nèi)存和性能開銷對(duì)比
// unique_ptr - 零開銷抽象 std::unique_ptr<Entity> uptr; // 大小:通常1個(gè)指針(8字節(jié)) // 開銷:無(wú)額外分配,析構(gòu)時(shí)直接delete // shared_ptr - 有顯著開銷 std::shared_ptr<Entity> sptr; // 大?。和ǔ?個(gè)指針(16字節(jié)) // 開銷:控制塊分配、引用計(jì)數(shù)原子操作
3. 避免意外的生命周期延長(zhǎng)
shared_ptr的陷阱
void process(const std::shared_ptr<Entity>& entity) {
// 如果內(nèi)部存儲(chǔ)了 shared_ptr,會(huì)意外延長(zhǎng)生命周期
background_tasks.store(entity); // 對(duì)象生命周期被意外延長(zhǎng)!
}
auto entity = std::make_shared<Entity>();
process(entity); // 可能造成生命周期問(wèn)題unique_ptr更安全
void process(std::unique_ptr<Entity> entity) {
// 明確:所有權(quán)被轉(zhuǎn)移,調(diào)用者失去所有權(quán)
// 不會(huì)意外共享所有權(quán)
}
4. 避免循環(huán)引用問(wèn)題
shared_ptr的循環(huán)引用
struct Node {
std::shared_ptr<Node> next;
// 可能形成循環(huán)引用,導(dǎo)致內(nèi)存泄漏
};
auto node1 = std::make_shared<Node>();
auto node2 = std::make_shared<Node>();
node1->next = node2;
node2->next = node1; // 循環(huán)引用!unique_ptr無(wú)此問(wèn)題
struct Node {
std::unique_ptr<Node> next; // 明確的所有權(quán)鏈
// 不可能形成循環(huán)引用
};
5. 代碼可維護(hù)性
unique_ptr使依賴關(guān)系清晰
class Game {
std::unique_ptr<Renderer> renderer_;
std::unique_ptr<PhysicsEngine> physics_;
public:
Game(std::unique_ptr<Renderer> renderer,
std::unique_ptr<PhysicsEngine> physics)
: renderer_(std::move(renderer))
, physics_(std::move(physics))
{}
// 明確:Game 獨(dú)占擁有這些組件
};6. 何時(shí)使用shared_ptr
雖然優(yōu)先選擇 unique_ptr,但 shared_ptr 在以下情況是合適的:
// 1. 真正的共享所有權(quán)
class TextureCache {
std::unordered_map<std::string, std::shared_ptr<Texture>> cache_;
public:
std::shared_ptr<Texture> getTexture(const std::string& name) {
// 多個(gè)地方可能同時(shí)使用同一個(gè)紋理
return cache_[name];
}
};
// 2. 需要弱引用的情況
std::shared_ptr<Connection> connection = createConnection();
std::weak_ptr<Connection> weak_conn = connection; // 觀察而不擁有
// 3. 與第三方API集成
void registerCallback(std::shared_ptr<Handler> handler);總結(jié)
設(shè)計(jì)原則:
- 默認(rèn)使用
unique_ptr- 除非明確需要共享所有權(quán) - 使用
shared_ptr- 當(dāng)確實(shí)需要多個(gè)所有者時(shí) - 使用
weak_ptr- 打破循環(huán)引用或觀察而不擁有
這種選擇策略能帶來(lái)更好的性能、更清晰的所有權(quán)語(yǔ)義和更少的資源管理錯(cuò)誤。
(注:文檔部分內(nèi)容可能由 AI 生成)
到此這篇關(guān)于C++ scoped_ptr 和 unique_ptr對(duì)比分析的文章就介紹到這了,更多相關(guān)C++ scoped_ptr 和 unique_ptr內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言實(shí)現(xiàn)動(dòng)態(tài)順序表詳解
這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)動(dòng)態(tài)順序表的實(shí)現(xiàn)代碼的相關(guān)資料,動(dòng)態(tài)順序表在內(nèi)存中開辟一塊空間,可以隨我們數(shù)據(jù)數(shù)量的增多來(lái)擴(kuò)容,需要的朋友可以參考下2021-08-08
C++實(shí)現(xiàn)LeetCode(169.求大多數(shù))
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(169.求大多數(shù)),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
C++實(shí)現(xiàn)將內(nèi)容寫入文件的方法總結(jié)
本文主要總結(jié)了一下C/C++將內(nèi)容寫入文件的方法,C的方法有些單調(diào),畢竟沒有庫(kù)函數(shù)。C++則豐富些,下面我把搜集到的整理一下,供大家參考2023-04-04
C語(yǔ)言學(xué)習(xí)之函數(shù)知識(shí)總結(jié)
函數(shù)是一組一起執(zhí)行一個(gè)任務(wù)的語(yǔ)句。每個(gè)?C?程序都至少有一個(gè)函數(shù),即主函數(shù)?main()?,所有簡(jiǎn)單的程序都可以定義其他額外的函數(shù)。本文就為大家詳細(xì)講講C語(yǔ)言中函數(shù)的相關(guān)知識(shí)點(diǎn),希望有所幫助2022-07-07
C++將字符串轉(zhuǎn)換為整數(shù)和浮點(diǎn)數(shù)的幾種方法
在日常編碼過(guò)程中,將字符串轉(zhuǎn)化為整數(shù)和浮點(diǎn)數(shù)是常見的需求,下面我們總結(jié)一下在c++中,字符串轉(zhuǎn)化為整數(shù)和浮點(diǎn)數(shù)的幾種方法,并通過(guò)代碼示例介紹的非常詳細(xì),需要的朋友可以參考下2025-07-07
C++中產(chǎn)生臨時(shí)對(duì)象的情況及其解決方案
這篇文章主要介紹了C++中產(chǎn)生臨時(shí)對(duì)象的情況及其解決方案,以值傳遞的方式給函數(shù)傳參,類型轉(zhuǎn)換以及函數(shù)需要返回對(duì)象時(shí),并給對(duì)應(yīng)給出了詳細(xì)的解決方案,通過(guò)圖文結(jié)合的方式講解的非常詳細(xì),需要的朋友可以參考下2024-05-05

