(原創)智慧型指標拾遺

2021-09-06 17:23:17 字數 3479 閱讀 4586

本文將介紹智慧型指標用法的一些平時可能沒注意的細節(關於智慧型指標的基本用法可以參考前面的博文)。

unique_ptr支援動態陣列,而shared_ptr不能直接支援動態陣列。std::unique_ptrptr(new int[10]);合法,而std::shared_ptrptr(new int[10]);是不合法的。

如果通過std::shared_ptr來構造動態陣列,則需要顯式指定刪除器,比如下面的**:

std::shared_ptrp(new int[10], (int* p)); //指定delete

也可以用std::default_delete作為刪除器:

std::shared_ptrp(new int[10], std::default_delete);

我們可以封裝乙個make_shared_array方法讓shared_ptr支援陣列:

templateshared_ptr

make_shared_array(size_t size)

測試**:

std::shared_ptr p = make_shared_array(10

);std::shared_ptr

p = make_shared_array(10);

unique_ptr缺少乙個類似於make_shared的make_unique方法,不過在c++14中會增加make_unique方法。其實要實現乙個make_unique方法是比較簡單的:

//

支援普通指標

templateinline

typename enable_if

::value, unique_ptr>::type make_unique(args&&... args)

//支援動態陣列

templateinline

typename enable_if

::value && extent::value==0, unique_ptr>::type

make_unique(size_t size)

//過濾掉定長陣列的情況

templatetypename enable_if

::value != 0, void>::type make_unique(args&&...) = delete;

實現思路很簡單,如果不是陣列則直接建立unique_ptr,如果是陣列的話,先判斷是否為定長陣列,如果為定長陣列則編譯不通過;非定長陣列時,獲取陣列中的元素型別,再根據入參size建立動態陣列的unique_ptr。extent::value用來獲取陣列的長度,如果獲取值為0,則不到說明不是定長陣列。

unique_ptr指定刪除器和std:: shared_ptr是有差別的,比如下面的寫法:

std:: shared_ptrptr(new int(1), (int*p)); //正確

std::unique_ptrptr(new int(1), (int*p)); //錯誤

std::unique_ptr指定刪除器的時候需要確定刪除器的型別,所以不能直接像shared_ptr指定刪除器,可以這樣寫:

std::unique_ptrptr(new int(1), (int*p));

上面這種寫法在lambda沒有捕獲變數的情況下是正確的,如果捕獲了變數則會編譯報錯:

std::unique_ptrptr(new int(1), [&](int*p)); //錯誤,因為捕獲了變數

為什麼lambda捕獲了變數作為unique_ptr就會報錯呢,因為lambda在沒有捕獲變數的情況下是可以直接轉換為函式指標的,捕獲了就不能轉換為函式指標。

如果希望unique_ptr的刪除器支援lambda,可以這樣寫:

std::unique_ptr> ptr(new int(1), [&](int*p));

我們還可以自定義unique_ptr的刪除器,比如下面的**:

#include #include 

using

namespace

std;

struct

mydeleter

};int

main()

智慧型指標可以很方便的管理當前程式庫動態分配的記憶體,還可以用來管理第三方庫分配的記憶體。第三方庫分配的記憶體一般需要通過第三方庫提供的釋放介面才能釋放,由於第三方庫返回出來的指標一般都是原始指標,如果用完之後沒有呼叫第三方庫的釋放介面,就很容易造成記憶體洩露。比如下面的**:

void* p = gethandle()->create();

//do something…

gethandle()->release(p);

這段**實際上是不安全的,在使用第三方庫分配的記憶體過程中,可能忘記呼叫release介面,還有可能中間不小心返回了,還有可能中間發生了異常,導致無法呼叫release介面,這時用智慧型指標去管理第三方庫的記憶體就很合適了,只要出了作用域記憶體就會自動釋放,不用顯式去呼叫釋放介面了,不用擔心中途返回或者發生異常導致無法呼叫釋放介面的問題。

void* p = gethandle()->create();

std::shared_ptr

sp(p, [this](void*p));

上面這段**就可以保證任何時候都能正確釋放第三方庫分配的記憶體。雖然能解決問題,但還是有些繁瑣,因為每個第三方庫分配記憶體的地方都要呼叫這段**,比較繁瑣,我們可以將這段**提煉出來作為乙個公共函式,簡化呼叫。

std::shared_ptr  guard(void*p));}

void* p = gethandle()->create();

auto sp =guard(p);

//do something…

上面的**通過guard函式做了簡化,用起來比較方便,但仍然不夠安全,因為有可能使用者可能會這樣寫:

void* p = gethandle()->create();

guard(p);

//危險,這句結束之後p就被釋放了

//do something…

這樣寫是有問題的,會導致訪問野指標,因為guard(p);是乙個右值,如果不賦值給乙個指標的話,guard(p);這句結束之後,就會釋放,導致p提前釋放了,後面就會訪問野指標的內容。auto sp = guard(p);需要乙個賦值操作,忘記賦值就會導致指標提前釋放,這種寫法仍然不夠安全。我們可以定義乙個巨集來解決這個問題:

#define guard(p) std::shared_ptrp##p(p, (void*p));

void* p = gethandle()->create();

guard(p);

//安全

也可以用unique_ptr來管理第三方的記憶體:

#define guard(p) std::unique_ptrp##p(p, (void*p));

通過巨集定義方式我們可以避免智慧型指標忘記賦值,即方便又安全。

C語言指標拾遺

指標這種東西,是需要記錄下來才能理解的比較清晰,所以專門記錄一下指標的一些知識點 指標理解 指標也是一種資料型別,和其他各種資料型別基本一樣 指標有幾個要素 指標的的值,就是該指標變數的值,該值就是乙個記憶體位址的編號,這個記憶體位址就是該指標所指向的變數所處的記憶體位址 指標變數的變數名,該變數名...

C 拾遺 類成員指標

c 拾遺 類成員指標 類成員的型別與一般型別不同,那麼類成員的指標型別自然與一般型別的指標有所區別。我們有必要來 下類成員指標的使用。類成員指標是指可以指向類的非靜態成員的指標。它的型別包括了類型別和所指向的成員型別。一般而言,指標指向的是物件,而類成員指標指向的是類成員而非類物件。需要指出,類成員...

asp知識拾遺

最近做的乙個小 碰到了許多問題,能解決的都解決了,還有的沒辦法就找了替代的解決辦法.下面收集一下 1 在選擇了一系列的checkbox後的提交表單提交後,會獲取到name1,逗號,空格,name2,逗號,空格,結果,系統需要獲取這些值,我用了以下 實現.uname request.form user...