溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

C++中unique_ptr如何使用

發(fā)布時間:2021-07-22 14:53:37 來源:億速云 閱讀:171 作者:Leah 欄目:互聯(lián)網(wǎng)科技

本篇文章為大家展示了C++中unique_ptr如何使用,內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。

unique_ptr 是 C++ 11 提供的用于防止內(nèi)存泄漏的智能指針中的一種實現(xiàn),獨享被管理對象指針?biāo)袡?quán)的智能指針。unique_ptr對象包裝一個原始指針,并負責(zé)其生命周期。當(dāng)該對象被銷毀時,會在其析構(gòu)函數(shù)中刪除關(guān)聯(lián)的原始指針。
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()
{
    // 通過原始指針創(chuàng)建 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 接受原始指針作為參數(shù)?,F(xiàn)在當(dāng)main函數(shù)退出時,該對象超出作用范圍就會調(diào)用其析構(gòu)函數(shù),在unique_ptr對象taskPtr 的析構(gòu)函數(shù)中,會刪除關(guān)聯(lián)的原始指針,這樣就不用專門delete Task對象了。
這樣不管函數(shù)正常退出還是異常退出(由于某些異常),也會始終調(diào)用taskPtr的析構(gòu)函數(shù)。因此,原始指針將始終被刪除并防止內(nèi)存泄漏。

unique_ptr 獨享所有權(quán)
unique_ptr對象始終是關(guān)聯(lián)的原始指針的唯一所有者。我們無法復(fù)制unique_ptr對象,它只能移動。
由于每個unique_ptr對象都是原始指針的唯一所有者,因此在其析構(gòu)函數(shù)中它直接刪除關(guān)聯(lián)的指針,不需要任何參考計數(shù)。

創(chuàng)建一個空的 unique_ptr 對象
創(chuàng)建一個空的unique_ptr<int>對象,因為沒有與之關(guān)聯(lián)的原始指針,所以它是空的。

std::unique_ptr<int> ptr1;

檢查 unique_ptr 對象是否為空
有兩種方法可以檢查 unique_ptr 對象是否為空或者是否有與之關(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;

使用原始指針創(chuàng)建 unique_ptr 對象
要創(chuàng)建非空的 unique_ptr 對象,需要在創(chuàng)建對象時在其構(gòu)造函數(shù)中傳遞原始指針,即:

std::unique_ptr<Task> taskPtr(new Task(22));

不能通過賦值的方法創(chuàng)建對象,下面的這句是錯誤的

// std::unique_ptr<Task> taskPtr2 = new Task(); // 編譯錯誤

使用 std::make_unique 創(chuàng)建 unique_ptr 對象 / C++14
std::make_unique<>() 是C++ 14 引入的新函數(shù)

std::unique_ptr<Task> taskPtr = std::make_unique<Task>(34);

獲取被管理對象的指針
使用get()·函數(shù)獲取管理對象的指針。

Task *p1 = taskPtr.get();

重置 unique_ptr 對象
在 unique_ptr 對象上調(diào)用reset()函數(shù)將重置它,即它將釋放delete關(guān)聯(lián)的原始指針并使unique_ptr 對象為空。

taskPtr.reset();

unique_ptr 對象不可復(fù)制
由于 unique_ptr 不可復(fù)制,只能移動。因此,我們無法通過復(fù)制構(gòu)造函數(shù)或賦值運算符創(chuàng)建unique_ptr對象的副本。

// 編譯錯誤 : unique_ptr 不能復(fù)制
std::unique_ptr<Task> taskPtr3 = taskPtr2; // Compile error

// 編譯錯誤 : unique_ptr 不能復(fù)制
taskPtr = taskPtr2; //compile error

轉(zhuǎn)移 unique_ptr 對象的所有權(quán)
我們無法復(fù)制 unique_ptr 對象,但我們可以轉(zhuǎn)移它們。這意味著 unique_ptr 對象可以將關(guān)聯(lián)的原始指針的所有權(quán)轉(zhuǎn)移到另一個 unique_ptr 對象。讓我們通過一個例子來理解:

// 通過原始指針創(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;

// 會輸出55
std::cout<< taskPtr4->mId << std::endl;

std::move() 將把 taskPtr2 轉(zhuǎn)換為一個右值引用。因此,調(diào)用 unique_ptr 的移動構(gòu)造函數(shù),并將關(guān)聯(lián)的原始指針傳輸?shù)?taskPtr4。在轉(zhuǎn)移完原始指針的所有權(quán)后, taskPtr2將變?yōu)榭铡?/p>

釋放關(guān)聯(lián)的原始指針
在 unique_ptr 對象上調(diào)用 release()將釋放其關(guān)聯(lián)的原始指針的所有權(quán),并返回原始指針。這里是釋放所有權(quán),并沒有delete原始指針,reset()會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()
{
    // 空對象 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

    // 通過原始指針創(chuàng)建 unique_ptr
    std::unique_ptr<Task> taskPtr(new Task(23));

    // 檢查 taskPtr 是否為空
    if(taskPtr != nullptr)
        std::cout<<"taskPtr is  not empty"<<std::endl;

    // 訪問 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();

    // 檢查是否為空 / 檢查有沒有關(guān)聯(lián)的原始指針
    if(taskPtr == nullptr)
        std::cout<<"taskPtr is  empty"<<std::endl;

    // 通過原始指針創(chuàng)建 unique_ptr
    std::unique_ptr<Task> taskPtr2(new Task(55));

    if(taskPtr2 != nullptr)
        std::cout<<"taskPtr2 is  not empty"<<std::endl;

    // unique_ptr 對象不能復(fù)制
    //taskPtr = taskPtr2; //compile error
    //std::unique_ptr<Task> taskPtr3 = taskPtr2;

    {
        // 轉(zhuǎn)移所有權(quán)(把unique_ptr中的指針轉(zhuǎn)移到另一個unique_ptr中)
        std::unique_ptr<Task> taskPtr4 = std::move(taskPtr2);
        // 轉(zhuǎn)移后為空
        if(taskPtr2 == nullptr)
            std::cout << "taskPtr2 is  empty" << std::endl;
        // 轉(zhuǎn)進來后非空
        if(taskPtr4 != nullptr)
            std::cout<<"taskPtr4 is not empty"<<std::endl;

        std::cout << taskPtr4->mId << std::endl;

        //taskPtr4 超出下面這個括號的作用于將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é)
new出來的對象是位于堆內(nèi)存上的,必須調(diào)用delete才能釋放其內(nèi)存。
unique_ptr 是一個裝指針的容器,且擁有關(guān)聯(lián)指針的唯一所有權(quán),作為普通變量使用時系統(tǒng)分配對象到棧內(nèi)存上,超出作用域時會自動析構(gòu),unique_ptr對象的析構(gòu)函數(shù)中會delete其關(guān)聯(lián)指針,這樣就相當(dāng)于替我們執(zhí)行了delete堆內(nèi)存上的對象。

成員函數(shù)    作用
reset()    重置unique_ptr為空,delete其關(guān)聯(lián)的指針。
release()    不delete關(guān)聯(lián)指針,并返回關(guān)聯(lián)指針。釋放關(guān)聯(lián)指針的所有權(quán),unique_ptr為空。
get()    僅僅返回關(guān)聯(lián)指針
unique_ptr不能直接復(fù)制,必須使用std::move()轉(zhuǎn)移其管理的指針,轉(zhuǎn)移后原 unique_ptr 為空。std::unique_ptr<Task> taskPtr4 = std::move(taskPtr2);

創(chuàng)建unique_ptr對象有兩種方法:

//C++11: 
std::unique_ptr<Task> taskPtr(new Task(23));
//C++14: 
std::unique_ptr<Task> taskPtr = std::make_unique<Task>(34);

上述內(nèi)容就是C++中unique_ptr如何使用,你們學(xué)到知識或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識儲備,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI