【Smart_Point】C/C++ 中独占指针unique_ptr
1. 獨占指針unique_ptr
目錄
1. 獨占指針unique_ptr
1.1 unique_ptr含義
1.2 C++11特性
1.3 C++14特性
1.1 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
23
Task::Destructor
unique_ptr <Task> 對象 taskPtr 接受原始指針作為參數。現在當main函數退出時,該對象超出作用范圍就會調用其析構函數,在unique_ptr對象taskPtr 的析構函數中,會刪除關聯的原始指針,這樣就不用專門delete Task對象了。 這樣不管函數正常退出還是異常退出(由于某些異常),也會始終調用taskPtr的析構函數。因此,原始指針將始終被刪除并防止內存泄漏。
1.2 C++11特性
1.2.1 unique_ptr 獨享所有權
unique_ptr對象始終是關聯的原始指針的唯一所有者。我們無法復制unique_ptr對象,它只能移動。 由于每個unique_ptr對象都是原始指針的唯一所有者,因此在其析構函數中它直接刪除關聯的指針,不需要任何參考計數。
創建一個空的 unique_ptr 對象
創建一個空的unique_ptr<int>對象,因為沒有與之關聯的原始指針,所以它是空的。
std::unique_ptr<int> ptr1;
1.2.2 檢查 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;
1.2.3 使用原始指針創建 unique_ptr 對象
要創建非空的 unique_ptr 對象,需要在創建對象時在其構造函數中傳遞原始指針,即:
std::unique_ptr<Task> taskPtr(new Task(22));
不能通過賦值的方法創建對象,下面的這句是錯誤的
std::unique_ptr<Task> taskPtr2 = new Task(); // 編譯錯誤
1.3 C++14特性
1.3.1 使用 std::make_unique 創建 unique_ptr 對象 / C++14
std::make_unique<>() 是C++ 14 引入的新函數
std::unique_ptr<Task> taskPtr = std::make_unique<Task>(34);
1.3.2 獲取被管理對象的指針
使用get()·函數獲取管理對象的指針。
Task *p1 = taskPtr.get();
1.3.3 重置 unique_ptr 對象
在 unique_ptr 對象上調用reset()函數將重置它,即它將釋放delete關聯的原始指針并使unique_ptr 對象為空。
taskPtr.reset();
1.3.4 unique_ptr 對象不可復制
由于 unique_ptr 不可復制,只能移動。因此,我們無法通過復制構造函數或賦值運算符創建unique_ptr對象的副本。
// 編譯錯誤 : unique_ptr 不能復制
std::unique_ptr<Task> taskPtr3 = taskPtr2; // Compile error// 編譯錯誤 : unique_ptr 不能復制
taskPtr = taskPtr2; //compile error
1.3.5 轉移 unique_ptr 對象的所有權
我們無法復制 unique_ptr 對象,但我們可以轉移它們。這意味著 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;// 通過原始指針創建 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;return 0;
}
輸出:
Task::Constructor
23
Task::Constructor
taskPtr2 is empty
taskPtr4 is not empty
55
Task::Destructor
Task::Destructor
std::move() 將把 taskPtr2 轉換為一個右值引用。因此,調用 unique_ptr 的移動構造函數,并將關聯的原始指針傳輸到 taskPtr4。在轉移完原始指針的所有權后, taskPtr2將變為空。
1.3.6 釋放關聯的原始指針
在 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_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;// 不能通過賦值初始化unique_ptr// std::unique_ptr<Task> taskPtr2 = new Task(); // Compile Error// 通過原始指針創建 unique_ptrstd::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_ptrstd::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
總結
以上是生活随笔為你收集整理的【Smart_Point】C/C++ 中独占指针unique_ptr的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【C++】C++对象模型:对象内存布局详
- 下一篇: 爷爷爷爷爷是什么歌啊