午夜视频在线网站,日韩视频精品在线,中文字幕精品一区二区三区在线,在线播放精品,1024你懂我懂的旧版人,欧美日韩一级黄色片,一区二区三区在线观看视频

分享

C 智能指針的底層實(shí)現(xiàn)原理

 新用戶73286115 2023-07-23 發(fā)布于北京

C++智能指針的頭文件:

#include <memory>

1. shared_ptr:

智能指針從本質(zhì)上來說是一個模板類,用類實(shí)現(xiàn)對指針對象的管理。

template <typename T>
class shared_ptr;

template <typename Y, class Deleter>
shared_ptr(Y* ptr, Deleter d);

template <typename Y, class Deleter, class Alloc>
shared_ptr(Y* ptr, Deleter d, Alloc alloc);

shared_ptr能解決的問題:

  1. 忘記釋放資源導(dǎo)致的內(nèi)存泄漏;

  2. 多個指針指向同一資源時可能產(chǎn)生的懸垂指針;

  3. (待補(bǔ)充)。。。

1.1 從避免出現(xiàn)懸垂指針引出shared_ptr的實(shí)現(xiàn)原理:

先來看一個普通指針可能出現(xiàn)的懸垂問題:

當(dāng)有多個指針指向同一個基礎(chǔ)對象時,如果某個指針delete了該基礎(chǔ)對象,對于其他指針來說,它們是無法感知的,此時則出現(xiàn)了懸垂指針,如果再對其他指針進(jìn)行操作,則可能會導(dǎo)致core dump。

(core dump的原因:因?yàn)橐呀?jīng)調(diào)用了delete,相當(dāng)于已經(jīng)將內(nèi)存資源歸還給了系統(tǒng),如果有其他地方向系統(tǒng)申請資源時,系統(tǒng)則重新分配這塊內(nèi)存。此時有兩種情況:① 原始的懸垂指針調(diào)用delete,系統(tǒng)檢測到二次釋放,直接core dump;② 原始的懸垂指針對指針地址上的內(nèi)存進(jìn)行讀、寫操作,可能意外的改寫了其他程序的內(nèi)容,即“踩內(nèi)存”,導(dǎo)致發(fā)生意想不到的情況。)

int* ptr1 = new int(42);
int* ptr2 = ptr1;
int* ptr3 = ptr1;

cout << *ptr1 << endl;
cout << *ptr2 << endl;
cout << *ptr3 << endl; //沒有問題,三個指針指向同一塊內(nèi)存地址

delete ptr1; //通過ptr1釋放了內(nèi)存資源,ptr2和ptr3成為懸垂指針

//...

cout << *ptr2 << endl; //可能沒問題,可能有問題

普通指針出現(xiàn)懸垂的根本原因在于:當(dāng)多個指針同時指向同一個內(nèi)存資源時,如果通過其中的某一個指針delete釋放了資源,其他指針無法感知到。

解決方法自然想到了“引用計(jì)數(shù)” ---- 通過一塊額外的內(nèi)存,實(shí)現(xiàn)對原始內(nèi)存的管理。

在這塊 “控制塊” 內(nèi)存中,保存當(dāng)前對原始內(nèi)存資源的引用計(jì)數(shù)。

普通指針多指針場景下出現(xiàn)懸垂指針的原因:

圖片

引入“控制塊”,保存對于基礎(chǔ)對象的“引用計(jì)數(shù)”,示例中有ptr1、ptr2、ptr3三個指針同時指向同一基礎(chǔ)對象,因此對應(yīng)這個基礎(chǔ)對象的引用計(jì)數(shù)為 3:

圖片

當(dāng)有某個指針退出作用域,或調(diào)用了delete釋放資源時,系統(tǒng)并非真正的釋放基礎(chǔ)對象,而是對引用計(jì)數(shù)減一。

那么何時才可以刪除基礎(chǔ)對象呢?當(dāng)只有一個指針指向基礎(chǔ)對象的時候,就可以大大方方的通過該指針將基礎(chǔ)對象刪除(真正的調(diào)用delete釋放基礎(chǔ)對象的資源)。

圖片

對于“控制塊”的實(shí)現(xiàn)方式:

圖片

對“控制塊”中“引用計(jì)數(shù)”的管理:

1、構(gòu)造函數(shù):

當(dāng)創(chuàng)建類的新對象時,初始化指針,并將引用計(jì)數(shù)設(shè)置為 1;

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

當(dāng)對象作為另一個對象的副本時(即發(fā)生“拷貝構(gòu)造”時),拷貝構(gòu)造函數(shù)拷貝副本指針,并對引用計(jì)數(shù) 加1;

3、拷貝賦值運(yùn)算符:

當(dāng)使用“拷貝賦值運(yùn)算符”(=)時,處理復(fù)雜一點(diǎn):

a. 先使“左操作數(shù)”的指針的引用計(jì)數(shù)減1 (為何減一:因?yàn)樵撝羔樢呀?jīng)指向別的地方,則指向原基礎(chǔ)對象的指針個數(shù)減1), 如果減1后引用計(jì)數(shù)降為0,則釋放指針?biāo)笇ο蟮膬?nèi)存資源;

b. 然后增加“右操作數(shù)”所指對象的引用計(jì)數(shù)(為何加一:因?yàn)榇藭r左操作數(shù)轉(zhuǎn)而指向此基礎(chǔ)對象,則指向此基礎(chǔ)對象的指針個數(shù)加1);

4、析構(gòu)函數(shù):

調(diào)用析構(gòu)函數(shù)時,析構(gòu)函數(shù)先使引用計(jì)數(shù)減1,如果減至0則delete釋放對象。

shared_ptr類的“構(gòu)造函數(shù)”使得基礎(chǔ)對象的引用計(jì)數(shù)遞增,shared_ptr類的“析構(gòu)函數(shù)”使得基礎(chǔ)對象的引用計(jì)數(shù)遞減。

當(dāng)最后一個指向基礎(chǔ)對象的shared_ptr被析構(gòu)時,會調(diào)用delete釋放基礎(chǔ)對象的內(nèi)存資源。

需要C/C++ Linux服務(wù)器架構(gòu)師學(xué)習(xí)資料加qun812855908獲?。ㄙY料包括C/C++,Linux,golang技術(shù),Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協(xié)程,DPDK,ffmpeg等),免費(fèi)分享

圖片

1.2 make_shared:

注意 make_shared 是 函數(shù)模板,不是類模板,make_shared函數(shù)模板的返回值類型是 shared_ptr。

template<typename T>
shared_ptr<T> make_shared(); //make_shared()模板函數(shù),返回一個shared_ptr<T> 類型的返回值

template<typename T, typename... Args>
shared_ptr<T> make_shared(Args&&... args);


//例如:
shared_ptr<string> p1 = make_shared<string>(10, '9');
shared_ptr<string> p2 = make_shared<string>('hello');
shared_ptr<string> p3 = make_shared<string>();

make_shared的優(yōu)點(diǎn):

  1. 效率更高;

  2. 異常安全。

make_shared的缺點(diǎn):

  1. 構(gòu)造函數(shù)是保護(hù)或私有時,無法使用make_shared;

  2. 對象內(nèi)存可能無法及時回收。

1.2.1 優(yōu)點(diǎn):效率更高:

假設(shè)原始對象類型為 widget,shared_ptr的“控制塊”中需要維護(hù)的關(guān)于“引用計(jì)數(shù)”的信息包括:

  1. 強(qiáng)引用:用來計(jì)數(shù)當(dāng)前有多少存活的shared_ptr正持有該對象,共享的對象會在最后一個強(qiáng)引用離開的時候釋放;

  2. 弱引用:用來記錄當(dāng)前有多少個正在觀察該對象的weak_ptr,當(dāng)最后一個弱引用離開的時候,共享的內(nèi)部信息控制塊會被釋放。

如果通過原始的new表達(dá)式分配對象,然后傳遞給shared_ptr(即使用shared_ptr內(nèi)部的構(gòu)造函數(shù)),則“控制塊內(nèi)存”與“基礎(chǔ)對象內(nèi)存”是分離開的,如圖所示:

此時是兩個分配內(nèi)存的動作,所以控制塊與基礎(chǔ)對象的內(nèi)存是分離的(可能造成內(nèi)存碎片)??刂茐K的內(nèi)存是在shared_ptr的構(gòu)造函數(shù)中分配的。

auto p = new widget(); //先使用new表達(dá)式分配一個widget類型的對象

shared_ptr<widget> sp1(p); //用p去初始化sp1,此時引用計(jì)數(shù)為1
shared_ptr<widget> sp2(sp1); //用sp1去拷貝初始化sp2,此時引用計(jì)數(shù)為2

圖片

如果使用 make_shared 的方式,則只需要一次分配內(nèi)存,分配出的內(nèi)存結(jié)構(gòu)如圖所示:

auto sp1 = make_shared<widget>();
auto sp2(sp1);

圖片

1.2.2 優(yōu)點(diǎn):異常安全:

可能會出現(xiàn)異常的情況:

//函數(shù)F的定義:
void F(shared_ptr<Lhs>& lhs, shared_ptr<Rhs>& rhs) { ... }

//調(diào)用F函數(shù):
F(shared_ptr<Lhs>(new Lhs('foo')), shared_ptr<Rhs>(new Rhs('bar')));

C++是不保證參數(shù)求值順序,以及內(nèi)部表達(dá)式的求值順序,所以可能的執(zhí)行順序如下:

1. new Lhs('foo')
2. new Rhs('bar')
3. shared_ptr<Lhs>
4. shared_ptr<Rhs>

此時,如果程序在第2步時拋出一個異常(比如out of memory等,Rhs的構(gòu)造函數(shù)異常的),那么在第1步中new分配的Lhs對象內(nèi)存將無法釋放,導(dǎo)致內(nèi)存泄漏。

這個問題的核心在于 shared_ptr 沒有立即獲得new分配出來的裸指針,shared_ptr與new結(jié)合使用時是要分成兩步。

修復(fù)這個問題的方式有兩種:

(1)不要將new操作放到函數(shù)形參初始化中,這樣將無法保證求值順序:

//解決方法是先保證兩個new分配內(nèi)存都沒有錯誤,并在new之后立即初始化shared_ptr:
auto lhs = shared_ptr<Lhs>(new Lhs('foo'));
auto rhs = shared_ptr<Rhs>(new Rhs('bar'));
F(lhs, rhs);

(2)更推薦的方法,是使用make_shared,一步到位 :

F(make_shared<Lhs>('foo'), make_shared<Rhs>('bar'))

1.2.3 缺點(diǎn):構(gòu)造函數(shù)是保護(hù)或私有時無法使用:

當(dāng)我們想要創(chuàng)建的對象沒有公有的構(gòu)造函數(shù)時,make_shared就無法使用了。

1.2.4 對象內(nèi)存可能無法及時回收:

make_shared的優(yōu)點(diǎn)是只需申請一次內(nèi)存,帶來了性能上的提升。但這一性能同樣也給make_shared帶來了缺點(diǎn):

智能指針的“控制塊”中保存著兩類關(guān)于“引用計(jì)數(shù)”的信息:

  1. 強(qiáng)引用;(strong refs)

  2. 弱引用。(weak refs)

“弱引用計(jì)數(shù)”用來保存當(dāng)前正在指向此基礎(chǔ)對象的weak_ptr指針的個數(shù),weak_ptr會保持控制塊的生命周期,因此有一種特殊情況是:強(qiáng)引用的引用計(jì)數(shù)已經(jīng)降為0,沒有shared_ptr再持有基礎(chǔ)對象,然而由于仍有weak_ptr指向基礎(chǔ)對象,弱引用的引用計(jì)數(shù)非0,原本因?yàn)閺?qiáng)引用計(jì)數(shù)已經(jīng)歸0就可以釋放的基礎(chǔ)對象內(nèi)存,現(xiàn)在變成了“強(qiáng)引用、弱引用都減為0時才能釋放”, 意外的延遲了內(nèi)存釋放的時間。這對于內(nèi)存要求高的場景來說,是一個需要注意的問題。

(一般情況下,程序中無需考慮這種微小的差別。)

1.3 shared_ptr實(shí)現(xiàn)說明:

摘自cppreference:

在典型的實(shí)現(xiàn)中,shared_ptr 只保有兩個指針:

  1. get()所返回的指針;(基礎(chǔ)對象的內(nèi)存地址)

  2. 指向控制塊的指針。(控制塊對象的內(nèi)存地址)

控制塊是一個動態(tài)分配的對象,其中包含:

  1. 指向被管理對象的指針或被管理對象本身;(基礎(chǔ)對象的內(nèi)存地址)

  2. 刪除器;(Deleter,類型擦除)

  3. 分配器;(Allocator,類型擦除)

  4. 占用被管理對象的shared_ptr的數(shù)量(strong refs強(qiáng)引用的引用計(jì)數(shù));

  5. 涉及被管理對象的weak_ptr的數(shù)量(weak refs弱引用的引用計(jì)數(shù)) 。

1.4 shared_ptr的線程安全性:

多線程環(huán)境下,調(diào)用不同shared_ptr實(shí)例的 成員函數(shù)是不需要額外的同步手段的(例如use_count()等成員函數(shù)),即使這些shared_ptr擁有的是同樣的對象。

但是,如果多線程訪問(有寫操作)同一個shared_ptr,則需要線程同步,否則就會有race condition發(fā)生。

shared_ptr的引用計(jì)數(shù)本身是安全且無鎖的,但shared_ptr中封裝的基礎(chǔ)對象的讀寫則不是。

出現(xiàn)這種情況的原因是:shared_ptr有兩個數(shù)據(jù)成員(指向被管理對象的指針,和指向控制塊的指針),讀寫操作不能原子化。

1.5 shared_from_this:

1.5.1 多個shared_ptr管理同一指針時的重復(fù)釋放問題:

在使用shared_ptr管理指針時,有一個原則就是要盡量避免“先new、后用裸指針初始化shared_ptr” 的方式,這是因?yàn)楫?dāng)有兩個或多個shared_ptr同時管理一個指針時,多個shared_ptr之間無法共享彼此的引用計(jì)數(shù),導(dǎo)致可能造成double free。

異常場景示例:(兩個shared_ptr共同管理同一個裸指針)

int main() {
int *ptr = new int(42);

shared_ptr<int> sp1(ptr);
shared_ptr<int> sp2(ptr);

cout << sp1.use_count() << endl;
cout << sp2.use_count() << endl;
//sp1記錄的引用計(jì)數(shù)是1,sp2記錄的引用計(jì)數(shù)也是1,
//此時有兩個智能指針sp1和sp2同時管理ptr,相當(dāng)于有兩個獨(dú)立的控制塊

return 0;
//此時退出作用域,sp1、sp2會分別調(diào)用delete去釋放基礎(chǔ)對象*ptr,
//重復(fù)釋放,導(dǎo)致程序段錯誤
}

由此引出一個使用shared_ptr的原則:

當(dāng)我們使用智能指針管理資源時,必須統(tǒng)一使用智能指針,而不能在某些地方使用智能指針,某些地方使用raw pointer,否則不能保持智能指針管理這個類對象的語義,從而產(chǎn)生各種錯誤。

給shared_ptr管理的資源必須在分配時立即交給shared_ptr,即:shared_ptr sp(new T());,而不是先new出ptr,再在后面的某個地方將ptr賦給shared_ptr。

1.5.1 shared_from_this的使用場景:

上述的情況同樣可能會發(fā)生在 this指針 上面。

當(dāng)一個類被shared_ptr管理(當(dāng)使用shared_ptr管理類對象時,實(shí)際上是管理的類對象的 *this指針),且在類的成員函數(shù)中需要把當(dāng)前類對象作為參數(shù)傳遞給其他函數(shù)時,就需要返回當(dāng)前對象的this指針,但是,直接傳遞this指針(相當(dāng)于裸指針)到類外,有可能會被多個shared_ptr所管理,造成與上面一樣的二次釋放的異常錯誤。

錯誤示例:

//C是一個可以返回類對象this指針的類:
class C {
public:
C(int b = 10) : a(b) { cout << 'constructor' << endl; }
~C() { cout << 'destructor' << endl; }

void show() const { cout << 'a = ' << a << endl; }
C* object_ptr() { return this; } //一個返回*this指針的成員函數(shù)

private:
int a;
};


int main() {
shared_ptr<C> sp1(new C(42)); //構(gòu)造一個C類對象,并由shared_ptr對此對象資源進(jìn)行管理

shared_ptr<C> sp2(sp1->object_ptr());
//在某種場景下返回類對象的this指針給其他函數(shù),我們的本意是在原有C類對象的基礎(chǔ)上累加引用計(jì)數(shù)
cout << sp1.use_count() << ', ' << sp2.use_count() << endl;
//sp1、sp2的引用計(jì)數(shù)都是 1

return 0;
//在退出程序前sp1、sp2的引用計(jì)數(shù)都降為0,會分別調(diào)用delete去釋放C類對象,導(dǎo)致重復(fù)釋放,段錯誤
}

出現(xiàn)上述異常的原因很簡單,類的成員函數(shù)將對象的this指針返回出去,this是一個普通指針,交給智能指針sp2管理,而sp2根本感知不到這個裸指針已經(jīng)被其他智能指針sp1給管理起來了。

使用shared_ptr直接管理this指針導(dǎo)致“重復(fù)釋放”的原因在于:

  1. 使用智能指針管理“類對象”的本質(zhì)是管理類對象的 this 指針;

  2. this指針與其他的普通裸指針并無區(qū)別,當(dāng)多個shared_ptr同時管理同一個this指針時,相互之間無法感知。

C++11 引入shared_from_this,使用方式如下:

  1. 繼承 enable_shared_from_this 類;

  2. 調(diào)用 shared_from_this() 成員函數(shù)先將this指針封裝進(jìn)一個shared_ptr,再將shared_ptr返回到類外供其他人使用。

使用shared_from_this 改寫上面的錯誤示例:

//首先,繼承enable_shared_from_this模板類:
//注意繼承模板類時需要先將類模板實(shí)例化,否則編譯器無法知道具體的數(shù)據(jù)類型
class C : public enable_shared_from_this<C> {
public:
C(int b = 10) : a(b) { cout << 'constructor' << endl; }
~C() { cout << 'destructor' << endl; }

void show() const { cout << 'a = ' << a << endl; }
//C* object_ptr() { return this; } //一個返回*this指針的成員函數(shù)
//在需要返回類對象this指針的地方,調(diào)用shared_from_this成員函數(shù)先將this封裝成shared_ptr再返回
shared_ptr<C> object_ptr() { return shared_from_this(); }

private:
int a;
};


int main() {
shared_ptr<C> sp1(new C(42));
cout << 'sp1.use_count : ' << sp1.use_count() << endl;

shared_ptr<C> sp2(sp1->object_ptr());
cout << 'sp2.use_count : ' << sp2.use_count() << endl;

return 0;
}

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

constructor
sp1.use_count : 1
sp2.use_count : 2
destructor

shared_from_this的使用公式為:

class A : public enable_shared_from_this<A> {
public:
shared_ptr<A> object_ptr() {
return shared_from_this();
}
};

上面的例子中,雖然在類成員函數(shù) object_ptr() 中先將this指針封裝成了一個shared_ptr,但是this指針的引用計(jì)數(shù)并沒有因此而比正常時多1,這涉及到shared_from_this的實(shí)現(xiàn)原理。

1.5.2 shared_from_this的實(shí)現(xiàn)原理:

要實(shí)現(xiàn)上述的shared_from_this 的功能,首先要考慮兩個設(shè)計(jì)原則:

1、首先要考慮的是:

在類對象本身當(dāng)中不能存儲類對象本身的shared_ptr,否則類對象shared_ptry永遠(yuǎn)也不會為0,從而這些資源永遠(yuǎn)不會釋放,除非程序結(jié)束。

2、其次,類對象肯定是外部函數(shù)通過某種機(jī)制分配的,而且一經(jīng)分配立即交給shared_ptr管理(強(qiáng)調(diào):給shared_ptr管理的資源必須在分配時交給shared_ptr),而且以后凡是需要共享使用類對象的地方必須使用這個shared_ptr當(dāng)作右值來構(gòu)造生產(chǎn)或者拷貝產(chǎn)生另一個shared_ptr從而達(dá)到共享使用的目的。

基于以上兩點(diǎn)要求,boost中使用的是 weak_ptr 的方式來實(shí)現(xiàn)的。

boost 1.39.0 中是這樣實(shí)現(xiàn)的:

1、首先生成類A:會依次調(diào)用 enable_shared_from_this 的構(gòu)造函數(shù) 以及 類A的構(gòu)造函數(shù)。

enable_shared_from_this 類中有一個 weak_ptr 成員,在enable_shared_from_this構(gòu)造函數(shù)中對其初始化,此時weak_ptr無效的,不指向任何對象。

2、接著,外部程序會把指向類A 對象的this指針作為初始化參數(shù)來初始化一個shared_ptr,就是下面的過程:

shared_ptr<A> sp(new A(42));

關(guān)鍵點(diǎn)在于這個shared_ptr如何初始化, shared_ptr模板類中定義了如下的構(gòu)造函數(shù):

template <typename Y>
explicit shared_ptr(Y *p) : px(p), pn(p)
{
boost::detail::sp_enable_shared_from_this(this, p, p);
}

//boost::detail::sp_enable_shared_from_this :
template <typename X, typename Y, typename T>
inline void sp_enable_shared_from_this(boost::shared_ptr<X> const * ppx,
Y const * py, boost::enable_shared_from_this< T > const * pe)
{
if( pe != 0 )
{
pe->_internal_accept_owner( ppx, const_cast< Y* >( py ) );
}
}


//_internal_accept_owner
template<class X, class Y> void _internal_accept_owner( shared_ptr<X> const * ppx, Y * py ) const
{
if( weak_this_.expired() )
{
weak_this_ = shared_ptr<T>( *ppx, py );
}
}

而在這里對 enable_shared_from_this 的成員weak_ptr進(jìn)行拷貝賦值,使得整個 weak_ptr作為類對象 shared_ptr的一個觀察者。這時,當(dāng)類對象本身需要自身的shared_ptr時,就可以從這個weak_ptr來生成一個了。

2. weak_ptr:

2.1 weak_ptr的特性:

  1. weak_ptr 只能從shared_ptr構(gòu)建;

  2. weak_ptr 并不影響動態(tài)對象的生命周期,即其存在與否并不影響對象的引用計(jì)數(shù)(強(qiáng)引用的引用計(jì)數(shù));

  3. weak_ptr 沒有重載 operator-> 和 operator* 操作符,因此不可以直接通過 weak_ptr 使用對象(必須通過weak_ptr獲取到shared_ptr后才能訪問基礎(chǔ)對象);

  4. weak_ptr 提供了 expired() 和 lock() 成員函數(shù),分別用于判斷基礎(chǔ)對象是否已被銷毀、返回指向基礎(chǔ)對象的shared_ptr指針。

weak_ptr 模板類中常用的成員函數(shù):

use_count : 與 shared_ptr.use_count() 的功能類似,返回指向基礎(chǔ)對象的shared_ptr的個數(shù)
對于一個“空”weak_ptr(還未指向任何shared_ptr),它的use_count是0

expired : 用于判斷weak_ptr所指向的對象是否已經(jīng)被銷毀(即判斷use_count是否為0),返回值為bool類型
如果所指對象已被銷毀,返回值為1(true)。對于“空”weak_ptr,expired返回值為1

lock : 返回weak_ptr所指向的基礎(chǔ)對象上的一個shared_ptr指針
如果對象已經(jīng)被銷毀,則返回“空”shared_ptr
如果成功返回一個shared_ptr,則強(qiáng)引用計(jì)數(shù)加1

2.2 weak_ptr的使用場景:

  1. 當(dāng)你想使用對象,但是并不管理對象,并且在需要的時候可以返回對象的shared_ptr時,則使用

  2. 解決shared_ptr的“循環(huán)引用”問題。

3. unique_ptr 與 auto_ptr:

智能指針可分為兩類:

  1. 獨(dú)占型: 如 unique_ptr,一份資源僅能由一個unique_ptr管理;

  2. 共享型: 如 shared_ptr,一份資源可以有多個shared_ptr共同管理,當(dāng)沒有shared_ptr對象指向這份資源時,資源才會釋放,即基于引用計(jì)數(shù)原理。

C++11中共有四種智能指針:auto_ptr、unique_ptr、shared_ptr、weak_ptr。

所有這些智能指針都是為了管理動態(tài)分配對象的生命周期而設(shè)計(jì)的,換言之,通過保證這樣的對象在適當(dāng)?shù)臅r機(jī)以適當(dāng)?shù)姆绞轿鰳?gòu)(包括發(fā)生異常的場合),來防止資源泄漏。

auto_ptr是個從c++98中殘留下來的棄用特性,它是一種對智能指針進(jìn)行標(biāo)準(zhǔn)化的嘗試,這種嘗試后來成為了c++11中的unique_ptr。

要正確的完成這個特性就需要移動語義,但在c++98中卻并沒有這樣的語義。作為一種變通手段,auto_ptr使用了 拷貝復(fù)制操作 來完成移動任務(wù),這就導(dǎo)致:

  1. 可能在運(yùn)行期產(chǎn)生異常的代碼(原始auto_ptr實(shí)例中的裸指針被置空);

  2. 某些使用限制(例如 不能在容器中存儲auto_ptr對象);

3.1 auto_ptr的核心實(shí)現(xiàn)代碼:

template <typename _Tp>
class auto_ptr {
private:
_Tp *_M_ptr;

public:
typedef _Tp element_type;

explicit auto_ptr(element_type *__p = 0) : _M_ptr(__p) { } //默認(rèn)構(gòu)造函數(shù)

auto_ptr(auto_ptr& __a) throw() : _M_ptr(__a.release()) { } //拷貝構(gòu)造函數(shù)。release()成員函數(shù)會返回auto_ptr內(nèi)封裝的裸指針地址

auto_ptr& operator=(auto_ptr& __a) throw() { //賦值運(yùn)算符.將原始auto_ptr實(shí)例中的裸指針置為空指針
reset(__a.release());
return *this;
}

element_type* release() throw() { //當(dāng)本類的實(shí)例作為參數(shù)給另外的auto_ptr賦值時,會調(diào)用實(shí)例的release成員函數(shù),將自身內(nèi)封裝的裸指針置為空,并返回資源的地址給新的auto_ptr實(shí)例
element_type *__tmp = _M_ptr;
_M_ptr = 0;
return __tmp;
}

void reset(element_type* __p = 0) throw() {
if(__p != _M_ptr) {
delete _M_ptr;
_M_ptr = __p;
}
}

};

3.2 unique_ptr的核心實(shí)現(xiàn)代碼:

unique_ptr的設(shè)計(jì)主要有如下兩點(diǎn):

  1. 禁止拷貝構(gòu)造函數(shù)、拷貝賦值運(yùn)算符,即設(shè)置為=delete;

  2. 實(shí)現(xiàn)了移動構(gòu)造函數(shù)和移動賦值運(yùn)算符。

unique_ptr必須直接初始化,且不能通過隱式轉(zhuǎn)換來構(gòu)造,因?yàn)閡nique_ptr的構(gòu)造函數(shù)被聲明為explicit。

//unique_ptr 有兩個版本:管理單個對象 或 管理動態(tài)分配的對象數(shù)組:
template <typename _Tp, typename _Dp = default_delete<_Tp>>
class unique_ptr { /****/ };

template <typename _Tp, typename _Dp>
class unique_ptr<_Tp[], _Dp> { /****/ };

3.3 unique_ptr 的常用操作:

u.get(); //返回unique_ptr中保存的裸指針
u.reset(); //重置unique_ptr
u.release(); //放棄指針的控制權(quán),返回裸指針,并將unique_ptr自身置為空。
u.swap(); //交換兩個unique_ptr所指向的對象

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多