国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > C++ > 正文

C++中的智能指針

2019-11-08 02:27:28
字體:
來源:轉載
供稿:網友

智能指針(Smart Pointer)智能在哪里

標簽(空格分隔): 學習筆記


傳統指針存在諸多的問題,比如指針所指向的對象的生命周期問題,掛起引用(dangling references),以及內存泄露(memory leaks). 如下代碼是一個傳統指針的使用過程

void Foo(){ int * ptr = new int[5]; //... //... delete[] ptr;}

以上代碼將正常運行且內存被合理釋放,但是在我們編寫較為復雜的程序時,申請了過多的東戴指針和動態內存,往往會忘了delete,或者在錯誤的時間錯誤的地點提前delete掉了指針,這無疑是會導致程序崩潰或者內存泄露的。 智能指針是RAII(Resource Acquisition is initialization)用來動態的分配內存。它提供了普通指針的所有接口外加少數異常處理。在構造階段,它將分配內存,而在非其作用域內將自動釋放所占有的內存。 在C++98中,使用 auto_ptr來解決上述問題。

1. auto_ptr

auto_ptr在所指對象作用域結束之后會自動調用其析構函數,這樣就不用手動的delete,看如下代碼:

#include <iostream>#include <memory>using namespace std;class autoPtrTest{public: //constructor function autoPtrTest(int a = 0) :m_a(a){} //deconstructor function ~autoPtrTest() { cout << "calling deconstructor" << endl; getchar(); }public: int m_a;};int main(){ //create an auto_ptr(p) to point a autoPtrTest class, and initialize with 5 auto_ptr<autoPtrTest> p(new autoPtrTest(5)); cout << p->m_a << endl; return 0;}

運行結果如下圖所示: 捕獲.PNG-31.3kB 當類型為auto_ptr的指針p指向的對象作用域結束后,自動調用了該類的析構函數. 但是,使用auto_ptr時,無可避免的會出現以下幾個問題,這是由auto_ptr自身的性質決定的 1)auto_ptr會傳遞它本身的ownership,當其被賦值給另一個auto_ptr對象。正如下述程序所示,一個auto_ptr對象傳遞給函數Fun()中的auto_ptr對象時,其ownership,或者說是smartness將不再返回給原auto_ptr所指向的p。

#include <iostream>#include <memory>using namespace std;class autoPtrTest{public: //constructor function autoPtrTest(int a = 0) :m_a(a){} //deconstructor function ~autoPtrTest() { cout << "calling deconstructor" << endl; }public: int m_a;};void Fun(auto_ptr<autoPtrTest> p1){ cout << p1->m_a << endl; cout << "Fun()end" << endl;}int main(){ auto_ptr<autoPtrTest> p(new autoPtrTest(5)); Fun(p); cout << p->m_a << endl; return 0;}

程序運行結果如下圖所示: 捕獲.PNG-42.8kB 這是因為在調用函數Fun時,main中創建的指針p在函數中將自己的所有權給了p1,所以離開p1的作用域時,編譯器自動調用了析構函數,此時main中p為一個空指的野指針,所以程序報錯。 2)auto_ptr不能使用于數組對象。 這里的意思是不能使用于操作符new[] 3)auto_ptr不能使用于一些標準的容器庫。比如vector,list,map等等 C++11提出了新型的智能指針,并且都賦予了其相應的意圖。

2.shared_ptr

shared_ptr設計的目的很簡單:多個共享指針可以指向同一個對象,而當最后一個共享指針在作用域范圍內結束時,內存才會被自動的釋放。

int main(){ // share_ptr 常規的創建過程 shared_ptr<int> sptr1(new int); // 使用make_shared 來加速創建過程 // shared_ptr 自動分配內存,并且保證引用計數 // 而make_shared則是按照這種方法來初始化 shared_ptr<int> sptr2 = make_shared<int>(100); // 可以通過use_count() 來查看引用計數 cout << "sptr2 referenced count: " << sptr2.use_count() << endl; shared_ptr<int> sptr3 = sptr2; cout << "sptr2 referenced count: " << sptr2.use_count() << endl; cout << "*sptr2 = " << *sptr2 << endl; getchar(); return 0;}

上述代碼的運行結果為: 捕獲.PNG-12.7kB 上述代碼創建了一個shared_ptr指針指向了一個裝著整型值且值為100的內存塊,并且引用計數為1,。當其他共享指針通過sptr1來創建時,引用計數將為2。

#include <iostream>#include <memory>using namespace std;class Test {public: Test(int a = 0) : m_a(a) {} ~Test() { cout << "Calling destructor" << endl; }public: int m_a;};int main(){ // 如果用以下形式,則只會調用delete,則不會調用 // delete[ ]; 此時只會調用一次析構函數 shared_ptr<Test> sptr1(new Test[5]); // 采用以下形式,lambda表達式,顯示調用 // delete[] 來刪除所有的對象。 shared_ptr<Test> sptr2(new Test[5], [](Test* p) {delete[] p;}); return 0;}

用戶可以顯式的調用函數,lambda表達式,函數對象來調用對于shared_ptr為數組對象的析構函數delete[]。 同時,shared_ptr為用戶提供了以下方便接口: shared_ptr提供解引用*, 以及->來普通指針的相關操作。同時,還提供了以下的接口: get(): To get the resource associated with the shared_ptr. reset(): To yield the ownership of the associated memory block. If this is the last shared_ptrowning the resource, then the resource is released automatically. unique: To know whether the resource is managed by only this shared_ptr instance. Operator bool: To check whether the shared_ptr owns a memory block or not. Can be used with an if condition. 但是,shared_ptr同樣也存在問題: 1)當一個內存塊與shared_ptr綁定相關,并且屬于不同組時,將會發生錯誤。所有的shared_ptr共享一個組的同一個共享引用。

int main(){ shared_ptr<int> sptr1(new int); shared_ptr<int> sptr2 = sptr1; shared_otr<int> sptr3; sptr3 = sptr2; return 0;}

以下表格給出了相應的引用計數:

pointer count
shared_ptrstPR1(new int) 1
shared_ptr sptr2 = sptr1 2
sptr3 = sptr2 3
when main ends->sptr3 goes out of scope 2
sptr2 goes out of scope 1
sptr1 goes out of scope 0->the resources is released

以上代碼運行正常,然而當運行以下代碼是:

int main(){ int *p = new int; shared_ptr<int>sptr1(p); shared_ptr<int>sptr2(p); return 0;}
pointer count
shared_ptrsptr1(p) 1
shared_ptrsptr2(p) 1
when main ends->sptr1 goes out of scope 0 p is destroyed
sptr2->goes out of scope 0 and crash

為了避免這種情況發生,最好不用從裸指針中建立共享指針。 2)問題2:另一個問題是,正如上述問題,如果從一個裸指針中創建一個共享指針,只有一個共享指針時,可以正常運行,但是當裸指針被釋放時,共享指針也會crash。 3)循環引用時,如果資源被非恰當釋放,也會出現問題。

#include <iostream>#include <memory>using namespace std;class B;class A{public: A() : m_sptrB(nullptr) {} ; ~A() { cout << "A is destroyed" << endl; } shared_ptr<B> m_sptrB;};class B{public: B() : m_sptrA(nullptr) {}; ~B() { cout << "B is destroyed" << endl; } shared_ptr<A> m_sptrA;};int main(){ shared_ptr<B> sptrB(new B); shared_ptr<A> sptrA(new A); sptrB->m_sptrA = sptrA; sptrA->m_sptrB = sptrB; return 0;}

當類A包含了指向B的共享指針,而類B包含了指向A 的恭喜那個指針時,sptrA和SptrB相關的資源都將不會被釋放。結果如下圖:

pointer count
shared_ptr sptrB(new B) 1
shared_ptr sptrA(new A) 1
sptrB->m_sptrA = sptrA sptrA->2
sptrA->m_sptrB = sptrB sptrB->2
main ends->sptrA goes out of scope sptrA->1
main ends->sptrB gos out of scope sptrB->1

3. weak_ptr

一個弱指針,提供的是一種共享語義定義而不是擁有語義定義。這就意味著一個弱指針可以通過shared_ptr共享資源。所以要創建弱指針,必須是已經擁有資源但是是一個共享指針。

一個弱指針并不允許諸如普通指針所提供的*和->。因為他并不是資源的擁有者。

那么如何利用弱指針呢? **weak_ptr只能用于跟蹤一個共享的資源,但并不實際擁有,也不會阻礙資源的釋放。讀取共享資源前需要先執行lock,得到shared_ptr后才能進行訪問。 當兩個對象需要互相引用時,我們總希望其中一個對象擁有另一個對象的強引用,而另一個對象擁有自己的弱引用,如果兩個對象都是強引用,則容易引起循環引用,導致兩個對象都無法正確釋放。**

用weak_ptr作為一個類似share_ptr但卻能懸浮的指針 有一個矛盾,一個靈巧指針可以像shared_ptr 一樣方便,但又不參與管理被指對象的所有權。換句話說,需要一個像shared_ptr但又不影響對象引用計數的指針。這類指針會有一個shared_ptr沒有的問題:被指的對象有可能已經被銷毀。一個良好的靈巧指針應該能處理這種情況,通過跟蹤什么時候指針會懸浮,比如在被指對象不復存在的時候。這正是weak_ptr這類型靈巧指針所能做到的。

weak_ptr一般是通過shared_ptr來構造的。當使用shared_ptr來初始化weak_ptr時,weak_ptr就指向了相同的地方,但是不改變所指對象的引用計數。 20150723144259417-22.3kB 從上圖可以看書,通過將一個weak_ptr賦值給另一個時會增加其弱引用計數。

如果弱引用指針所指向的資源,被其共享指針所釋放時,這時候弱指針將會過期。如何檢測一個弱指針是否指向一個合法的資源呢?有以下兩種途徑。

調用use_count()來得到引用計數。注意這里返回的是強引用計數。 調用expired()函數,這比調用use_count要快的多。 同時,我們可以通過對一個weak_ptr調用函數lock()來得到一個shared_ptr。或者直接對一個weak_ptr進行強制轉換。

int main(){ shared_ptr<A> sptr<new A> weak_ptr<A> wptr(sptr); shared_ptr<A> sptr2 = wptr.lock(); shared_ptr<B> sptrB(new B); shared_ptr<A> sptrA(new A); sptrB->m_sptrA = sptrA; sptrA->m_sptrB = sptrB; return 0;}

以上方法將增加強引用計數 以下例子將展示如何使用weak_ptr解決循環引用問題

#include <iostream>#include <memory>using namespace std;class B;class A{public: A() : m_a(5) {} ; ~A() { cout << "A is destroyed" << endl; } void PrintSpB() ; weak_ptr<B> m_sptrB; int m_a;};class B {public: B() : m_b(10) {} ; ~B() { cout << "B is destroyed" << endl; } weak_ptr<A> m_sptrA; int m_b;};void A::PrintSpB(){ if( !m_sptrB.expired() ) cout << m_sptrB.lock()->m_b << endl;}int main(){ shared_ptr<B> sptrB(new B); shared_ptr<A> sptrA(new A); sptrB->m_sptrA = sptrA; sptrA->m_sptrB = sptrB; sptrA->PrintSpB(); return 0;}

4.unique_ptr

unique_ptr幾乎是易出錯的auto_ptr的另一種形式。unique_ptr遵循專用所有權語義。在任何時刻,資源只被唯一的一個unique_ptr所占有。當auto_ptr不在作用域范圍內時,資源就會被釋放。當一個資源被其他資源重寫時,如果先前的資源已經被釋放,這保證了相關的資源也會被釋放。 creation(創建) unique_ptr創建的過程和shared_ptr創建的過程大同小異,所不同的是創建的數組形式的對象。 unique_ptr提供了專用創建數組對象的析構調用delete[]而不是delete當其不在作用域范圍內。

unique_ptr<int[]>unptr(new int[5]);

對于資源的擁有權(ownership)可以從一個unique_ptr通過另一個進行賦值來傳遞。

需要記住的是:unique_ptr并不提供復制機制copy semantics(包括復制賦值copy assignment以及復制構造函數copy construction)而是一種移動機制。

Interface(接口)

unique_ptr提供的接口與普通常規指針的接口非常相似,但是并不提供指針運算。 unique_ptr提供release()函數來進行yield the ownership。release()和reset()函數的區別在于,reset()會對資源進行銷毀。

參考文獻:


上一篇:c++學習筆記

下一篇:C++中的常量函數

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

主站蜘蛛池模板: 鄂温| 安国市| 芜湖市| 泗阳县| 济源市| 沙雅县| 富锦市| 佛山市| 黄冈市| 姚安县| 玛沁县| 西充县| 西乡县| 峨边| 吐鲁番市| 泽州县| 沂源县| 仁布县| 蓬安县| 伊金霍洛旗| 连云港市| 台北县| 浠水县| 东乌珠穆沁旗| 仁寿县| 曲松县| 正定县| 彭泽县| 天峨县| 伊宁市| 额敏县| 清镇市| 隆子县| 清镇市| 会昌县| 镇远县| 高平市| 兰考县| 高雄县| 同江市| 上虞市|