最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++手?jǐn)]智能指針的教程分享

 更新時(shí)間:2023年05月21日 16:49:57   作者:會(huì)玩code  
在前文中小編為大家介紹了C++智能指針的一些使用方法和基本原理,所以本文就來自己動(dòng)手,從0到1實(shí)現(xiàn)一下自己的unique_ptr和shared_ptr吧

前言

大家好,今天是【重學(xué)C++】的第三講,書接上回,第二講《02 脫離指針陷阱:深入淺出 C++ 智能指針》介紹了C++智能指針的一些使用方法和基本原理。今天,我們自己動(dòng)手,從0到1實(shí)現(xiàn)一下自己的unique_ptrshared_ptr。

回顧

智能指針的基本原理是基于RAII設(shè)計(jì)理論,自動(dòng)回收內(nèi)存資源,從根本上避免內(nèi)存泄漏。在第一講《01 C++ 如何進(jìn)行內(nèi)存資源管理?》介紹RAII的時(shí)候,就已經(jīng)給了一個(gè)用于封裝int類型指針,實(shí)現(xiàn)自動(dòng)回收資源的代碼實(shí)例:

class?AutoIntPtr?{
public:
????AutoIntPtr(int*?p?=?nullptr)?:?ptr(p)?{}
????~AutoIntPtr()?{?delete?ptr;?}
????int&?operator*()?const?{?return?*ptr;?}
????int*?operator->()?const?{?return?ptr;?}
private:
????int*?ptr;
};

我們從這個(gè)示例出發(fā),一步步完善我們自己的智能指針。

模版化

這個(gè)類有個(gè)明顯的問題:只能適用于int類指針。所以我們第一步要做的,就是把它改造成一個(gè)類模版,讓這個(gè)類適用于任何類型的指針資源。code show time

template?<typename?T>
class?smart_ptr?{
public:
?explicit?smart_ptr(T*?ptr?=?nullptr):?ptr_(ptr)?{}
?~smart_ptr()?{
??delete?ptr_;
?}
?T&?operator*()?const?{?return?*ptr_;?}
?T*?operator->()?const?{?return?ptr_;?}
private:
?T*?ptr_;
}

我給我們的智能指針類用了一個(gè)更抽象,更切合的類名:smart_ptr。

AutoIntPtr相比,我們把smart_ptr設(shè)計(jì)成一個(gè)類模版,原來代碼中的int改成模版參數(shù)T,非常簡(jiǎn)單。使用時(shí)也只要把AutoIntPtr(new int(9)) 改成smart_ptr<int>(new int(9))即可。

另外,有一點(diǎn)值得注意,smart_ptr的構(gòu)造函數(shù)使用了explicit, explicit關(guān)鍵字主要用于防止隱式的類型轉(zhuǎn)換。代碼中,如果原生指針隱式地轉(zhuǎn)換為智能指針類型可能會(huì)導(dǎo)致一些潛在的問題。至于會(huì)有什么問題,你那聰明的小腦瓜看完下面的代碼肯定能理解了:

void?foo(smart_ptr<int>?int_ptr)?{
????//?...
}
int?main()?{
????int*?raw_ptr?=?new?int(42);
????foo(raw_ptr);??//?隱式轉(zhuǎn)換為?smart_ptr<int>
????std::cout?<<?*raw_ptr?<<?std::endl;???//?error:?raw_ptr已經(jīng)被回收了
????//?...
}

假設(shè)我們沒有為smart_ptr構(gòu)造函數(shù)加上explicit,原生指針raw_ptr在傳給foo函數(shù)后,會(huì)被隱形轉(zhuǎn)換為smart_ptr<int>, foo函數(shù)調(diào)用結(jié)束后,棲構(gòu)入?yún)⒌?code>smart_ptr<int>時(shí)會(huì)把raw_ptr給回收掉了,所以后續(xù)對(duì)raw_ptr的調(diào)用都會(huì)失敗。

拷貝還是移動(dòng)

當(dāng)前我們沒有為smart_ptr自定義拷貝構(gòu)造函數(shù)/移動(dòng)構(gòu)造函數(shù),C++會(huì)為smart_ptr生成默認(rèn)的拷貝/移動(dòng)構(gòu)造函數(shù)。默認(rèn)的拷貝/移動(dòng)構(gòu)造函數(shù)邏輯很簡(jiǎn)單:把每個(gè)成員變量拷貝/移動(dòng)到目標(biāo)對(duì)象中。

按當(dāng)前smart_ptr的實(shí)現(xiàn),我們假設(shè)有以下代碼:

smart_ptr<int>?ptr1{new?int(10)};
smart_ptr<int>?ptr2?=?ptr1;

這段代碼在編譯時(shí)不會(huì)出錯(cuò),問題在運(yùn)行時(shí)才會(huì)暴露出來:第二行將ptr1管理的指針復(fù)制給了ptr2,所以會(huì)重復(fù)釋放內(nèi)存,導(dǎo)致程序奔潰。

為了避免同一塊內(nèi)存被重復(fù)釋放。解決辦法也很簡(jiǎn)單:

  • 獨(dú)占資源所有權(quán),每時(shí)每刻一個(gè)內(nèi)存對(duì)象(資源)只能有一個(gè)smart_ptr占有它。
  • 一個(gè)內(nèi)存對(duì)象(資源)只有在最后一個(gè)擁有它的smart_ptr析構(gòu)時(shí)才會(huì)進(jìn)行資源回收。

獨(dú)占所有權(quán) - unique_smart_ptr

獨(dú)占資源的所有權(quán),并不是指禁用掉smart_ptr的拷貝/移動(dòng)函數(shù)(當(dāng)然這也是一種簡(jiǎn)單的避免重復(fù)釋放內(nèi)存的方法)。而是smart_ptr在拷貝時(shí),代表資源對(duì)象的指針不是復(fù)制到另外一個(gè)smart_ptr,而是"移動(dòng)"到新smart_ptr。移動(dòng)后,原來的smart_ptr.ptr_ == nullptr, 這樣就完成了資源所有權(quán)的轉(zhuǎn)移。這也是C++ unique_ptr的基本行為。我們?cè)谶@里先把它命名為unique_smart_ptr,代碼完整實(shí)現(xiàn)如下:

template?<typename?T>
class?unique_smart_ptr?{
public:
?explicit?unique_smart_ptr(T*?ptr?=?nullptr):?ptr_(ptr)?{}
?~unique_smart_ptr()?{
??delete?ptr_;
?}
?//?1.?自定義移動(dòng)構(gòu)造函數(shù)
?unique_smart_ptr(unique_smart_ptr&&?other)?{
??//?1.1?把other.ptr_?賦值到this->ptr_
??ptr_?=?other.ptr_;
??//?1.2?把other.ptr_指為nullptr,other不再擁有資源指針
??other.ptr_?=?nullptr;
?}
?//?2.?自定義賦值行為
?unique_smart_ptr&?operator?=?(unique_smart_ptr?rhs)?{
??//?2.1?交換rhs.ptr_和this->ptr_
??std::swap(rhs.ptr_,?this->ptr_);
??return?*this;
?}
T&?operator*()?const?{?return?*ptr_;?}
T*?operator->()?const?{?return?ptr_;?}
private:
?T*?ptr_;
};

自定義移動(dòng)構(gòu)造函數(shù)。在移動(dòng)構(gòu)造函數(shù)中,我們先是接管了other.ptr_指向的資源對(duì)象,然后把otherptr_置為nullptr,這樣在other析構(gòu)時(shí)就不會(huì)錯(cuò)誤釋放資源內(nèi)存。

同時(shí),根據(jù)C++的規(guī)則,手動(dòng)提供移動(dòng)構(gòu)造函數(shù)后,就會(huì)自動(dòng)禁用拷貝構(gòu)造函數(shù)。也就是我們能得到以下效果:

unique_smart_ptr<int>?ptr1{new?int(10)};
unique_smart_ptr<int>?ptr2?=?ptr1;?//?error
unique_smart_ptr<int>?ptr3?=?std::move(ptr1);?//?ok
unique_smart_ptr<int>?ptr4{ptr1}?//?error
unique_smart_ptr<int>?ptr5{std::move(ptr1)}?//?ok

自定義賦值函數(shù)。在賦值函數(shù)中,我們使用std::swap交換了 rhs.ptr_this->ptr_,注意,這里不能簡(jiǎn)單的將rhs.ptr_設(shè)置為nullptr,因?yàn)?code>this->ptr_可能有指向一個(gè)堆對(duì)象,該對(duì)象需要轉(zhuǎn)給rhs,在賦值函數(shù)調(diào)用結(jié)束,rhs析構(gòu)時(shí)順便釋放掉。避免內(nèi)存泄漏。

注意賦值函數(shù)的入?yún)?code>rhs的類型是unique_smart_ptr而不是unique_smart_ptr&&,這樣創(chuàng)建rhs使用移動(dòng)構(gòu)造函數(shù)還是拷貝構(gòu)造函數(shù)完全取決于unique_smart_ptr的定義。因?yàn)?code>unique_smart_ptr當(dāng)前只保留了移動(dòng)構(gòu)造函數(shù),所以rhs是通過移動(dòng)構(gòu)造函數(shù)創(chuàng)建的。

多個(gè)智能指針共享對(duì)象 - shared_smart_ptr

學(xué)過第二講的shared_ptr, 我們知道它是利用計(jì)數(shù)引用的方式,實(shí)現(xiàn)了多個(gè)智能指針共享同一個(gè)對(duì)象。當(dāng)最后一個(gè)持有對(duì)象的智能指針析構(gòu)時(shí),計(jì)數(shù)器減為0,這個(gè)時(shí)候才會(huì)回收資源對(duì)象。

我們先給出shared_smart_ptr的類定義

template?<typename?T>
class?shared_smart_ptr?{
public:
?//?構(gòu)造函數(shù)
?explicit?shared_smart_ptr(T*?ptr?=?nullptr)
?//?析構(gòu)函數(shù)
?~shared_smart_ptr()
?//?移動(dòng)構(gòu)造函數(shù)
?shared_smart_ptr(shared_smart_ptr&&?other)
?//?拷貝構(gòu)造函數(shù)
?shared_smart_ptr(const?shared_smart_ptr&?other)
?//?賦值函數(shù)
?shared_smart_ptr&?operator?=?(shared_smart_ptr?rhs)
?//?返回當(dāng)前引用次數(shù)
?int?use_count()?const?{?return?*count_;?}
?T&?operator*()?const?{?return?*ptr_;?}
?T*?operator->()?const?{?return?ptr_;?}
private:
?T*?ptr_;
?int*?count_;
}

暫時(shí)不考慮多線程并發(fā)安全的問題,我們簡(jiǎn)單在堆上創(chuàng)建一個(gè)int類型的計(jì)數(shù)器count_。下面詳細(xì)展開各個(gè)函數(shù)的實(shí)現(xiàn)。

為了避免對(duì)count_的重復(fù)刪除,我們保持:只有當(dāng)ptr_ != nullptr時(shí),才對(duì)count_進(jìn)行賦值。

構(gòu)造函數(shù)

同樣的,使用explicit避免隱式轉(zhuǎn)換。除了賦值ptr_, 還需要在堆上創(chuàng)建一個(gè)計(jì)數(shù)器。

explicit?shared_smart_ptr(T*?ptr?=?nullptr){
?ptr_?=?ptr;
?if?(ptr_)?{
??count_?=?new?int(1);
?}
}

析構(gòu)函數(shù)

在析構(gòu)函數(shù)中,需要根據(jù)計(jì)數(shù)器的引用數(shù)判斷是否需要回收對(duì)象。

~shared_smart_ptr()?{
?//?ptr_為nullptr,不需要做任何處理
?if?(ptr_)?{
??return;
?}
?//?計(jì)數(shù)器減一
?--(*count_);
?//?計(jì)數(shù)器減為0,回收對(duì)象
?if?(*count_?==?0)?{
??delete?ptr_;
??delete?count_;
??return;
?}
}

移動(dòng)構(gòu)造函數(shù)

添加對(duì)count_的處理

shared_smart_ptr(shared_smart_ptr&&?other)?{
?ptr_?=?other.ptr_;
?count_?=?other.count_;
?other.ptr_?=?nullptr;
?other.count_?=?nullptr;
}

賦值構(gòu)造函數(shù)

添加交換count_

shared_smart_ptr&?operator?=?(shared_smart_ptr?rhs)?{
?std::swap(rhs.ptr_,?this->ptr_);
?std::swap(rhs.count_,?this->count_);
?return?*this;
}

拷貝構(gòu)造函數(shù)

對(duì)于shared_smart_ptr,我們需要手動(dòng)支持拷貝構(gòu)造函數(shù)。主要處理邏輯是賦值ptr_和增加計(jì)數(shù)器的引用數(shù)。

shared_smart_ptr(const?shared_smart_ptr&?other)?{
?ptr_?=?other.ptr_;
?count_?=?other.count_;
?if?(ptr_)?{
??(*count_)++;
?}
}

這樣,我們就實(shí)現(xiàn)了一個(gè)自己的共享智能指針,貼一下完整代碼

template?<typename?T>
class?shared_smart_ptr?{
public:
?explicit?shared_smart_ptr(T*?ptr?=?nullptr){
??ptr_?=?ptr;
??if?(ptr_)?{
???count_?=?new?int(1);
??}
?}
?~shared_smart_ptr()?{
??//?ptr_為nullptr,不需要做任何處理
??if?(ptr_?==?nullptr)?{
???return;
??}
??//?計(jì)數(shù)器減一
??--(*count_);
??//?計(jì)數(shù)器減為0,回收對(duì)象
??if?(*count_?==?0)?{
???delete?ptr_;
???delete?count_;
??}
?}
?shared_smart_ptr(shared_smart_ptr&&?other)?{
??ptr_?=?other.ptr_;
??count_?=?other.count_;
??other.ptr_?=?nullptr;
??other.count_?=?nullptr;
?}
?shared_smart_ptr(const?shared_smart_ptr&?other)?{
??ptr_?=?other.ptr_;
??count_?=?other.count_;
??if?(ptr_)?{
???(*count_)++;
??}
?}
?shared_smart_ptr&?operator?=?(shared_smart_ptr?rhs)?{
??std::swap(rhs.ptr_,?this->ptr_);
??std::swap(rhs.count_,?this->count_);
??return?*this;
?}
?int?use_count()?const?{?return?*count_;?};
?T&?operator*()?const?{?return?*ptr_;?};
?T*?operator->()?const?{?return?ptr_;?};
private:
?T*?ptr_;
?int*?count_;
};

使用下面代碼進(jìn)行驗(yàn)證:

int?main(int?argc,?const?char**?argv)?{
?shared_smart_ptr<int>?ptr1(new?int(1));
?std::cout?<<?"[初始化ptr1]?use?count?of?ptr1:?"?<<?ptr1.use_count()?<<?std::endl;
?{
??//?賦值使用拷貝構(gòu)造函數(shù)
??shared_smart_ptr<int>?ptr2?=?ptr1;
??std::cout?<<?"[使用拷貝構(gòu)造函數(shù)將ptr1賦值給ptr2]?use?count?of?ptr1:?"?<<?ptr1.use_count()?<<?std::endl;
??//?賦值使用移動(dòng)構(gòu)造函數(shù)
??shared_smart_ptr<int>?ptr3?=?std::move(ptr2);
??std::cout?<<?"[使用移動(dòng)構(gòu)造函數(shù)將ptr2賦值給ptr3]?use?count?of?ptr1:?"?<<?ptr1.use_count()?<<?std::endl;
?}
?std::cout?<<?"[ptr2和ptr3析構(gòu)后]?use?count?of?ptr1:?"?<<?ptr1.use_count()?<<?std::endl;
}

運(yùn)行結(jié)果:

[初始化ptr1] use count of ptr1: 1
[使用拷貝構(gòu)造函數(shù)將ptr1賦值給ptr2] use count of ptr1: 2
[使用移動(dòng)構(gòu)造函數(shù)將ptr2賦值給ptr3] use count of ptr1: 2
[ptr2和ptr3析構(gòu)后] use count of ptr1: 1

總結(jié)

這一講我們從AutoIntPtr出發(fā),先是將類進(jìn)行模版化,使其能夠管理任何類型的指針對(duì)象,并給該類起了一個(gè)更抽象、更貼切的名稱——smart_ptr。

接著圍繞著「如何正確釋放資源對(duì)象指針」的問題,一步步手?jǐn)]了兩個(gè)智能指針 ——unique_smart_ptrshared_smart_ptr。相信大家現(xiàn)在對(duì)智能指針有一個(gè)較為深入的理解了。

以上就是C++手?jǐn)]智能指針的教程分享的詳細(xì)內(nèi)容,更多關(guān)于C++智能指針的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C++如何調(diào)用python并取返回值

    C++如何調(diào)用python并取返回值

    這篇文章主要介紹了C++如何調(diào)用python并取返回值問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • c++虛函數(shù)及常見問題匯總

    c++虛函數(shù)及常見問題匯總

    文章詳細(xì)介紹了面向?qū)ο缶幊讨械乃拇蠡咎匦裕悍庋b、抽象、繼承和多態(tài),重點(diǎn)解釋了多態(tài)的概念及其在C++中的實(shí)現(xiàn)方式,包括虛函數(shù)、虛函數(shù)表、單繼承、多繼承以及純虛函數(shù)的使用,通過代碼示例展示了多態(tài)在不同情況下的表現(xiàn),以及純虛函數(shù)的約束作用,感興趣的朋友一起看看吧
    2025-12-12
  • 詳解C++中十六進(jìn)制字符串轉(zhuǎn)數(shù)字(數(shù)值)

    詳解C++中十六進(jìn)制字符串轉(zhuǎn)數(shù)字(數(shù)值)

    這篇文章主要介紹了詳解C++中十六進(jìn)制字符串轉(zhuǎn)數(shù)字(數(shù)值)的相關(guān)資料,這里提供兩種實(shí)現(xiàn)方法,需要的朋友可以參考下
    2017-08-08
  • c++ fielsystem庫(kù)的具體使用

    c++ fielsystem庫(kù)的具體使用

    C++17引入了fielsystem庫(kù),它提供了一組用于操作文件和路徑的工具,這個(gè)庫(kù)極大地簡(jiǎn)化了文件和目錄的管理,使得在不同操作系統(tǒng)上進(jìn)行文件操作更加方便和統(tǒng)一,感興趣的可以了解一下
    2025-07-07
  • C++實(shí)現(xiàn)簡(jiǎn)單職工管理系統(tǒng)

    C++實(shí)現(xiàn)簡(jiǎn)單職工管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C++職工管理系統(tǒng)實(shí)訓(xùn)代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-04-04
  • C++迷宮問題的求解算法

    C++迷宮問題的求解算法

    這篇文章主要為大家詳細(xì)介紹了C++迷宮問題的求解算法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-03-03
  • C語(yǔ)言冒泡排序法的實(shí)現(xiàn)(升序排序法)

    C語(yǔ)言冒泡排序法的實(shí)現(xiàn)(升序排序法)

    這篇文章主要介紹了C語(yǔ)言冒泡排序法的實(shí)現(xiàn)(升序排序法),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • 基于字符串移位包含的問題詳解

    基于字符串移位包含的問題詳解

    本篇文章是對(duì)字符串移位包含的問題的解決方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • C語(yǔ)言超詳細(xì)講解循環(huán)與分支語(yǔ)句基礎(chǔ)

    C語(yǔ)言超詳細(xì)講解循環(huán)與分支語(yǔ)句基礎(chǔ)

    各位小伙伴們,今天給大家?guī)淼氖茄h(huán)與分支語(yǔ)句,本篇將會(huì)向大家介紹這些語(yǔ)句的格式和使用的基本方法,感興趣的朋友來看看吧
    2022-04-04
  • 探討數(shù)組與字符串輸入的問題(C++版)

    探討數(shù)組與字符串輸入的問題(C++版)

    本文對(duì)C++數(shù)組與字符串輸入的問題進(jìn)行了探討,需要的朋友可以參考下
    2015-07-07

最新評(píng)論

新密市| 永泰县| 河曲县| 铁力市| 北安市| 恩施市| 呈贡县| 松江区| 巴中市| 舞钢市| 晋宁县| 泸溪县| 丹棱县| 扶沟县| 安乡县| 徐州市| 孟连| 丰台区| 南靖县| 铜陵市| 柳江县| 美姑县| 贡觉县| 青川县| 始兴县| 桐柏县| 翼城县| 静宁县| 沈丘县| 浮山县| 韶关市| 郎溪县| 灵石县| 余庆县| 东辽县| 卢氏县| 武安市| 莱西市| 神池县| 通化县| 河西区|