您好,登錄后才能下訂單哦!
本篇文章為大家展示了C++中unique_ptr如何使用,內容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。
unique_ptr 是 C++ 11 提供的用于防止內存泄漏的智能指針中的一種實現,獨享被管理對象指針所有權的智能指針。unique_ptr對象包裝一個原始指針,并負責其生命周期。當該對象被銷毀時,會在其析構函數中刪除關聯的原始指針。
unique_ptr具有->和*運算符重載符,因此它可以像普通指針一樣使用。
查看下面的示例:
#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()
{
// 通過原始指針創建 unique_ptr 實例
std::unique_ptr<Task> taskPtr(new Task(23));
//通過 unique_ptr 訪問其成員
int id = taskPtr->mId;
std::cout << id << std::endl;
return 0;
}
輸出:
Task::Constructor
Task::Destructor
unique_ptr <Task> 對象 taskPtr 接受原始指針作為參數。現在當main函數退出時,該對象超出作用范圍就會調用其析構函數,在unique_ptr對象taskPtr 的析構函數中,會刪除關聯的原始指針,這樣就不用專門delete Task對象了。
這樣不管函數正常退出還是異常退出(由于某些異常),也會始終調用taskPtr的析構函數。因此,原始指針將始終被刪除并防止內存泄漏。
unique_ptr 獨享所有權
unique_ptr對象始終是關聯的原始指針的唯一所有者。我們無法復制unique_ptr對象,它只能移動。
由于每個unique_ptr對象都是原始指針的唯一所有者,因此在其析構函數中它直接刪除關聯的指針,不需要任何參考計數。
創建一個空的 unique_ptr 對象
創建一個空的unique_ptr<int>對象,因為沒有與之關聯的原始指針,所以它是空的。
std::unique_ptr<int> ptr1;
檢查 unique_ptr 對象是否為空
有兩種方法可以檢查 unique_ptr 對象是否為空或者是否有與之關聯的原始指針。
// 方法1
if(!ptr1)
std::cout<<"ptr1 is empty"<<std::endl;
// 方法2
if(ptr1 == nullptr)
std::cout<<"ptr1 is empty"<<std::endl;
使用原始指針創建 unique_ptr 對象
要創建非空的 unique_ptr 對象,需要在創建對象時在其構造函數中傳遞原始指針,即:
std::unique_ptr<Task> taskPtr(new Task(22));
不能通過賦值的方法創建對象,下面的這句是錯誤的
// std::unique_ptr<Task> taskPtr2 = new Task(); // 編譯錯誤
使用 std::make_unique 創建 unique_ptr 對象 / C++14
std::make_unique<>() 是C++ 14 引入的新函數
std::unique_ptr<Task> taskPtr = std::make_unique<Task>(34);
獲取被管理對象的指針
使用get()·函數獲取管理對象的指針。
Task *p1 = taskPtr.get();
重置 unique_ptr 對象
在 unique_ptr 對象上調用reset()函數將重置它,即它將釋放delete關聯的原始指針并使unique_ptr 對象為空。
taskPtr.reset();
unique_ptr 對象不可復制
由于 unique_ptr 不可復制,只能移動。因此,我們無法通過復制構造函數或賦值運算符創建unique_ptr對象的副本。
// 編譯錯誤 : unique_ptr 不能復制
std::unique_ptr<Task> taskPtr3 = taskPtr2; // Compile error
// 編譯錯誤 : unique_ptr 不能復制
taskPtr = taskPtr2; //compile error
轉移 unique_ptr 對象的所有權
我們無法復制 unique_ptr 對象,但我們可以轉移它們。這意味著 unique_ptr 對象可以將關聯的原始指針的所有權轉移到另一個 unique_ptr 對象。讓我們通過一個例子來理解:
// 通過原始指針創建 taskPtr2
std::unique_ptr<Task> taskPtr2(new Task(55));
// 把taskPtr2中關聯指針的所有權轉移給taskPtr4
std::unique_ptr<Task> taskPtr4 = std::move(taskPtr2);
// 現在taskPtr2關聯的指針為空
if(taskPtr2 == nullptr)
std::cout<<"taskPtr2 is empty"<<std::endl;
// taskPtr2關聯指針的所有權現在轉移到了taskPtr4中
if(taskPtr4 != nullptr)
std::cout<<"taskPtr4 is not empty"<<std::endl;
// 會輸出55
std::cout<< taskPtr4->mId << std::endl;
std::move() 將把 taskPtr2 轉換為一個右值引用。因此,調用 unique_ptr 的移動構造函數,并將關聯的原始指針傳輸到 taskPtr4。在轉移完原始指針的所有權后, taskPtr2將變為空。
釋放關聯的原始指針
在 unique_ptr 對象上調用 release()將釋放其關聯的原始指針的所有權,并返回原始指針。這里是釋放所有權,并沒有delete原始指針,reset()會delete原始指針。
std::unique_ptr<Task> taskPtr5(new Task(55));
// 不為空
if(taskPtr5 != nullptr)
std::cout<<"taskPtr5 is not empty"<<std::endl;
// 釋放關聯指針的所有權
Task * ptr = taskPtr5.release();
// 現在為空
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()
{
// 空對象 unique_ptr
std::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;
// 不能通過賦值初始化unique_ptr
// std::unique_ptr<Task> taskPtr2 = new Task(); // Compile Error
// 通過原始指針創建 unique_ptr
std::unique_ptr<Task> taskPtr(new Task(23));
// 檢查 taskPtr 是否為空
if(taskPtr != nullptr)
std::cout<<"taskPtr is not empty"<<std::endl;
// 訪問 unique_ptr關聯指針的成員
std::cout<<taskPtr->mId<<std::endl;
std::cout<<"Reset the taskPtr"<<std::endl;
// 重置 unique_ptr 為空,將刪除關聯的原始指針
taskPtr.reset();
// 檢查是否為空 / 檢查有沒有關聯的原始指針
if(taskPtr == nullptr)
std::cout<<"taskPtr is empty"<<std::endl;
// 通過原始指針創建 unique_ptr
std::unique_ptr<Task> taskPtr2(new Task(55));
if(taskPtr2 != nullptr)
std::cout<<"taskPtr2 is not empty"<<std::endl;
// unique_ptr 對象不能復制
//taskPtr = taskPtr2; //compile error
//std::unique_ptr<Task> taskPtr3 = taskPtr2;
{
// 轉移所有權(把unique_ptr中的指針轉移到另一個unique_ptr中)
std::unique_ptr<Task> taskPtr4 = std::move(taskPtr2);
// 轉移后為空
if(taskPtr2 == nullptr)
std::cout << "taskPtr2 is empty" << std::endl;
// 轉進來后非空
if(taskPtr4 != nullptr)
std::cout<<"taskPtr4 is not empty"<<std::endl;
std::cout << taskPtr4->mId << std::endl;
//taskPtr4 超出下面這個括號的作用于將delete其關聯的指針
}
std::unique_ptr<Task> taskPtr5(new Task(66));
if(taskPtr5 != nullptr)
std::cout << "taskPtr5 is not empty" << std::endl;
// 釋放所有權
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
總結
new出來的對象是位于堆內存上的,必須調用delete才能釋放其內存。
unique_ptr 是一個裝指針的容器,且擁有關聯指針的唯一所有權,作為普通變量使用時系統分配對象到棧內存上,超出作用域時會自動析構,unique_ptr對象的析構函數中會delete其關聯指針,這樣就相當于替我們執行了delete堆內存上的對象。
成員函數 作用
reset() 重置unique_ptr為空,delete其關聯的指針。
release() 不delete關聯指針,并返回關聯指針。釋放關聯指針的所有權,unique_ptr為空。
get() 僅僅返回關聯指針
unique_ptr不能直接復制,必須使用std::move()轉移其管理的指針,轉移后原 unique_ptr 為空。std::unique_ptr<Task> taskPtr4 = std::move(taskPtr2);
創建unique_ptr對象有兩種方法:
//C++11:
std::unique_ptr<Task> taskPtr(new Task(23));
//C++14:
std::unique_ptr<Task> taskPtr = std::make_unique<Task>(34);
上述內容就是C++中unique_ptr如何使用,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。