【Smart_Point】C/C++ 中独占指针unique_ptr
1. 獨(dú)占指針unique_ptr
目錄
1. 獨(dú)占指針unique_ptr
1.1 unique_ptr含義
1.2 C++11特性
1.3 C++14特性
1.1 unique_ptr含義
unique_ptr 是 C++ 11 提供的用于防止內(nèi)存泄漏的智能指針中的一種實(shí)現(xiàn),獨(dú)享被管理對(duì)象指針?biāo)袡?quán)的智能指針。unique_ptr對(duì)象包裝一個(gè)原始指針,并負(fù)責(zé)其生命周期。當(dāng)該對(duì)象被銷(xiāo)毀時(shí),會(huì)在其析構(gòu)函數(shù)中刪除關(guān)聯(lián)的原始指針。 unique_ptr具有->和*運(yùn)算符重載符,因此它可以像普通指針一樣使用。 查看下面的示例:
#include <iostream>
#include <memory>
?
struct Task {int mId;Task(int id ) :mId(id) {std::cout << "Task::Constructor" << std::endl;}~Task() {std::cout << "Task::Destructor" << std::endl;}
};
?
int main()
{// 通過(guò)原始指針創(chuàng)建 unique_ptr 實(shí)例std::unique_ptr<Task> taskPtr(new Task(23));
?//通過(guò) unique_ptr 訪(fǎng)問(wèn)其成員int id = taskPtr->mId;std::cout << id << std::endl;
?return 0;
}
對(duì)應(yīng)輸出:
Task::Constructor
23
Task::Destructor
unique_ptr <Task> 對(duì)象 taskPtr 接受原始指針作為參數(shù)。現(xiàn)在當(dāng)main函數(shù)退出時(shí),該對(duì)象超出作用范圍就會(huì)調(diào)用其析構(gòu)函數(shù),在unique_ptr對(duì)象taskPtr 的析構(gòu)函數(shù)中,會(huì)刪除關(guān)聯(lián)的原始指針,這樣就不用專(zhuān)門(mén)delete Task對(duì)象了。 這樣不管函數(shù)正常退出還是異常退出(由于某些異常),也會(huì)始終調(diào)用taskPtr的析構(gòu)函數(shù)。因此,原始指針將始終被刪除并防止內(nèi)存泄漏。
1.2 C++11特性
1.2.1 unique_ptr 獨(dú)享所有權(quán)
unique_ptr對(duì)象始終是關(guān)聯(lián)的原始指針的唯一所有者。我們無(wú)法復(fù)制unique_ptr對(duì)象,它只能移動(dòng)。 由于每個(gè)unique_ptr對(duì)象都是原始指針的唯一所有者,因此在其析構(gòu)函數(shù)中它直接刪除關(guān)聯(lián)的指針,不需要任何參考計(jì)數(shù)。
創(chuàng)建一個(gè)空的 unique_ptr 對(duì)象
創(chuàng)建一個(gè)空的unique_ptr<int>對(duì)象,因?yàn)闆](méi)有與之關(guān)聯(lián)的原始指針,所以它是空的。
std::unique_ptr<int> ptr1;
1.2.2 檢查 unique_ptr 對(duì)象是否為空
有兩種方法可以檢查 unique_ptr 對(duì)象是否為空或者是否有與之關(guān)聯(lián)的原始指針。
// 方法1
if(!ptr1)std::cout<<"ptr1 is empty"<<std::endl;
// 方法2
if(ptr1 == nullptr)std::cout<<"ptr1 is empty"<<std::endl;
1.2.3 使用原始指針創(chuàng)建 unique_ptr 對(duì)象
要?jiǎng)?chuàng)建非空的 unique_ptr 對(duì)象,需要在創(chuàng)建對(duì)象時(shí)在其構(gòu)造函數(shù)中傳遞原始指針,即:
std::unique_ptr<Task> taskPtr(new Task(22));
不能通過(guò)賦值的方法創(chuàng)建對(duì)象,下面的這句是錯(cuò)誤的
std::unique_ptr<Task> taskPtr2 = new Task(); // 編譯錯(cuò)誤
1.3 C++14特性
1.3.1 使用 std::make_unique 創(chuàng)建 unique_ptr 對(duì)象 / C++14
std::make_unique<>() 是C++ 14 引入的新函數(shù)
std::unique_ptr<Task> taskPtr = std::make_unique<Task>(34);
1.3.2 獲取被管理對(duì)象的指針
使用get()·函數(shù)獲取管理對(duì)象的指針。
Task *p1 = taskPtr.get();
1.3.3 重置 unique_ptr 對(duì)象
在 unique_ptr 對(duì)象上調(diào)用reset()函數(shù)將重置它,即它將釋放delete關(guān)聯(lián)的原始指針并使unique_ptr 對(duì)象為空。
taskPtr.reset();
1.3.4 unique_ptr 對(duì)象不可復(fù)制
由于 unique_ptr 不可復(fù)制,只能移動(dòng)。因此,我們無(wú)法通過(guò)復(fù)制構(gòu)造函數(shù)或賦值運(yùn)算符創(chuàng)建unique_ptr對(duì)象的副本。
// 編譯錯(cuò)誤 : unique_ptr 不能復(fù)制
std::unique_ptr<Task> taskPtr3 = taskPtr2; // Compile error// 編譯錯(cuò)誤 : unique_ptr 不能復(fù)制
taskPtr = taskPtr2; //compile error
1.3.5 轉(zhuǎn)移 unique_ptr 對(duì)象的所有權(quán)
我們無(wú)法復(fù)制 unique_ptr 對(duì)象,但我們可以轉(zhuǎn)移它們。這意味著 unique_ptr 對(duì)象可以將關(guān)聯(lián)的原始指針的所有權(quán)轉(zhuǎn)移到另一個(gè) unique_ptr 對(duì)象。讓我們通過(guò)一個(gè)例子來(lái)理解:
#include <iostream>
#include <memory>struct Task {int mId;Task(int id ) :mId(id) {std::cout << "Task::Constructor" << std::endl;}~Task() {std::cout << "Task::Destructor" << std::endl;}
};int main()
{// 通過(guò)原始指針創(chuàng)建 unique_ptr 實(shí)例std::unique_ptr<Task> taskPtr(new Task(23));//通過(guò) unique_ptr 訪(fǎng)問(wèn)其成員int id = taskPtr->mId;std::cout << id << std::endl;// 通過(guò)原始指針創(chuàng)建 taskPtr2
std::unique_ptr<Task> taskPtr2(new Task(55));
// 把taskPtr2中關(guān)聯(lián)指針的所有權(quán)轉(zhuǎn)移給taskPtr4
std::unique_ptr<Task> taskPtr4 = std::move(taskPtr2);
// 現(xiàn)在taskPtr2關(guān)聯(lián)的指針為空
if(taskPtr2 == nullptr)std::cout<<"taskPtr2 is empty"<<std::endl;// taskPtr2關(guān)聯(lián)指針的所有權(quán)現(xiàn)在轉(zhuǎn)移到了taskPtr4中
if(taskPtr4 != nullptr)std::cout<<"taskPtr4 is not empty"<<std::endl;// 會(huì)輸出55
std::cout<< taskPtr4->mId << std::endl;return 0;
}
輸出:
Task::Constructor
23
Task::Constructor
taskPtr2 is empty
taskPtr4 is not empty
55
Task::Destructor
Task::Destructor
std::move() 將把 taskPtr2 轉(zhuǎn)換為一個(gè)右值引用。因此,調(diào)用 unique_ptr 的移動(dòng)構(gòu)造函數(shù),并將關(guān)聯(lián)的原始指針傳輸?shù)?taskPtr4。在轉(zhuǎn)移完原始指針的所有權(quán)后, taskPtr2將變?yōu)榭铡?/p>
1.3.6 釋放關(guān)聯(lián)的原始指針
在 unique_ptr 對(duì)象上調(diào)用 release()將釋放其關(guān)聯(lián)的原始指針的所有權(quán),并返回原始指針。這里是釋放所有權(quán),并沒(méi)有delete原始指針,reset()會(huì)delete原始指針。
std::unique_ptr<Task> taskPtr5(new Task(55));
// 不為空
if(taskPtr5 != nullptr)std::cout<<"taskPtr5 is not empty"<<std::endl;
// 釋放關(guān)聯(lián)指針的所有權(quán)
Task * ptr = taskPtr5.release();
// 現(xiàn)在為空
if(taskPtr5 == nullptr)std::cout<<"taskPtr5 is empty"<<std::endl;
完整示例程序
#include <iostream>
#include <memory>struct Task {int mId;Task(int id ) :mId(id) {std::cout<<"Task::Constructor"<<std::endl;}~Task() {std::cout<<"Task::Destructor"<<std::endl;}
};int main()
{// 空對(duì)象 unique_ptrstd::unique_ptr<int> ptr1;// 檢查 ptr1 是否為空if(!ptr1)std::cout<<"ptr1 is empty"<<std::endl;// 檢查 ptr1 是否為空if(ptr1 == nullptr)std::cout<<"ptr1 is empty"<<std::endl;// 不能通過(guò)賦值初始化unique_ptr// std::unique_ptr<Task> taskPtr2 = new Task(); // Compile Error// 通過(guò)原始指針創(chuàng)建 unique_ptrstd::unique_ptr<Task> taskPtr(new Task(23));// 檢查 taskPtr 是否為空if(taskPtr != nullptr)std::cout<<"taskPtr is not empty"<<std::endl;// 訪(fǎng)問(wèn) unique_ptr關(guān)聯(lián)指針的成員std::cout<<taskPtr->mId<<std::endl;std::cout<<"Reset the taskPtr"<<std::endl;// 重置 unique_ptr 為空,將刪除關(guān)聯(lián)的原始指針taskPtr.reset();// 檢查是否為空 / 檢查有沒(méi)有關(guān)聯(lián)的原始指針if(taskPtr == nullptr)std::cout<<"taskPtr is empty"<<std::endl;// 通過(guò)原始指針創(chuàng)建 unique_ptrstd::unique_ptr<Task> taskPtr2(new Task(55));if(taskPtr2 != nullptr)std::cout<<"taskPtr2 is not empty"<<std::endl;// unique_ptr 對(duì)象不能復(fù)制//taskPtr = taskPtr2; //compile error//std::unique_ptr<Task> taskPtr3 = taskPtr2;{// 轉(zhuǎn)移所有權(quán)(把unique_ptr中的指針轉(zhuǎn)移到另一個(gè)unique_ptr中)std::unique_ptr<Task> taskPtr4 = std::move(taskPtr2);// 轉(zhuǎn)移后為空if(taskPtr2 == nullptr)std::cout << "taskPtr2 is empty" << std::endl;// 轉(zhuǎn)進(jìn)來(lái)后非空if(taskPtr4 != nullptr)std::cout<<"taskPtr4 is not empty"<<std::endl;std::cout << taskPtr4->mId << std::endl;//taskPtr4 超出下面這個(gè)括號(hào)的作用于將delete其關(guān)聯(lián)的指針}std::unique_ptr<Task> taskPtr5(new Task(66));if(taskPtr5 != nullptr)std::cout << "taskPtr5 is not empty" << std::endl;// 釋放所有權(quán)Task * ptr = taskPtr5.release();if(taskPtr5 == nullptr)std::cout << "taskPtr5 is empty" << std::endl;std::cout << ptr->mId << std::endl;delete ptr;return 0;
}
輸出
ptr1 is empty
ptr1 is empty
Task::Constructor
taskPtr is not empty
23
Reset the taskPtr
Task::Destructor
taskPtr is empty
Task::Constructor
taskPtr2 is not empty
taskPtr2 is empty
taskPtr4 is not empty
55
Task::Destructor
Task::Constructor
taskPtr5 is not empty
taskPtr5 is empty
66
Task::Destructor
總結(jié)
以上是生活随笔為你收集整理的【Smart_Point】C/C++ 中独占指针unique_ptr的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【C++】C++对象模型:对象内存布局详
- 下一篇: 【Smart_Point】C/C++ 中