日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

智能指针知识

發布時間:2024/3/26 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 智能指针知识 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

智能指針

智能指針的行為類似常規指針,重要的區別是它負責自動釋放所指向的對象。新標準提供的兩種智能指針的區別在于管理底層指針的方式。shared_ptr允許多個指針指向同一個對象;unique_ptr則“獨占”所指向的對象。標準庫還定義了一個名為weak_ptr的伴隨類,它是一種弱引用,指向shared_ptr所管理的對象。 這三個類型都定義在memory頭文件中。

文章目錄

  • 智能指針
  • shared_ptr
    • shared_ptr和unique_ptr都支持的操作
    • shared_ptr獨有的操作
    • make_shared函數
    • shared_ptr的拷貝和賦值
    • shared_ptr自動銷毀所管理的對象
    • shared_ptr和new結合使用
    • 智能指針陷阱
  • unique_ptr
  • weak_ptr

shared_ptr

類似vector,智能指針是模板。當我們創建一個智能指針時,必須提供額外的信息——指針可以指向的類型。

shared_ptr<string> p1; //shared_ptr,可以指向string shared_ptr<list<int>> p2; //shared_ptr,可以指向Int的list

默認初始化的智能指針中保存著一個空指針。

智能指針的使用方式與普通指針類似。解引用一個智能指針返回它指向的對象。如果在一個條件判斷中使用智能指針,效果就是檢測它是否為空。

shared_ptr和unique_ptr都支持的操作

shared_ptr<T> sp; //空智能指針,可以指向類型為T的對象 unique_ptr<T> up;p; //將p用作一個條件判斷,若p指向一個對象,則為true *p; //解引用p,獲得它所指向的對象p->mem; //等價于(*p).memp.get(); //返回p中保存的指針。要小心使用,若智能指針釋放了其對象,//返回的指針所指向的對象也就消失了 swap(p, q); //交換p和q中的指針 p.swap(q);

shared_ptr獨有的操作

make_shared<T>(args); //返回一個shared_ptr,指向一個動態分配的類型為T的對象//使用args初始化此對象shared_ptr<T>p(q); //p是shared_ptr q的拷貝;此操作會遞增q中的計數器。q中的指針//必須能夠轉換到T*;p = q; //p和q都是shared_ptr,所保存的指針必須能夠互相轉換。//此操作會遞減p的引用次數,遞增q的引用次數;若p的引用次數變為0//則將其管理的原內存釋放p.unique(); //若p.use_count()為1,返回true;否則返回false p.use_count(); //返回與p共享對象的智能指針數量;

make_shared函數

最安全的分配和使用動態內存的方法是調用一個名為make_shared的標準庫函數。此函數在動態內存中分配一個對象并初始化它,返回指向此對象的shared_ptr。

當要用make_shared時,必須指定想要創建的對象的類型。定義方式與模板類相同,在函數名之后跟一個尖括號,在其中給出類型。

shared_ptr<int> p3 = make_shared<int>(42); shared_ptr<string> p4 = make_shared<string>(10, '9'); shared_ptr<int> p5 = make_shared<int>();

類似順序容器的emplace成員,make_shared用其參數來構造給定類型的對象。例如,調用make_shared<string>時傳遞的參數必須與string的某個構造函數相匹配,調用make_shared<int>時傳遞的參數必須能用來初始化int,依此類推,如果我們不傳遞任何參數,對象就會進行值初始化。

shared_ptr的拷貝和賦值

當進行拷貝或賦值操作時,每個shared_ptr都會記錄有多少個其他shared_ptr指向相同的對象。

auto p = make_shared<int>(42); //p指向的對象只有p一個引用者 auto q(p); //p和q指向相同對象,此對象有兩個引用者

可以認為每個shared_ptr都有一個關聯的計數器。通常稱為引用計數,無論何時拷貝一個shared_ptr,計數器都會遞增。無論何時我們拷貝一個shared_ptr,計數器都會遞增。例如,當用一個shared_ptr初始化另一個shared_ptr,或將它作為參數傳遞給一個函數以及作為函數的返回值時,它所關聯的計數器就會遞增,當我們給shared_ptr賦予一個新值或者是shared_ptr被銷毀(例如一個局部的shared_ptr離開作用域)時,計數器就會遞減。

一旦一個shared_ptr的計數器變為0,它就會自動釋放自己所管理的對象。

shared_ptr自動銷毀所管理的對象

當指向一個對象的最后一個shared_ptr被銷毀時,shared_ptr類會自動銷毀此對象。它是通過另一個特殊的成員函數——析構函數完成銷毀工作的。

shared_ptr的析構函數會遞減它所指向的對象的引用次數。如果引用計數變為0,shared_ptr的析構函數就會銷毀對象,并釋放它所占用的內存。

shared_ptr和new結合使用

如果我們不初始化一個智能指針,它就會被初始化為一個空指針。

shared_ptr<double> p1; //shared_ptr可以指向一個double shared_ptr<int> p2(new int(42)); //p2指向一個值為42的int

接受指針參數的智能指針構造函數是explicit的(拷貝賦值也是explicit)。因此我們不能將一個內置指針隱式轉換為一個智能指針,必須使用直接初始化。

shared_ptr<int> p1 = new int(1024); //錯誤:必須使用直接初始化形式 shared_ptr<int> p2(new int(1024)); //正確:使用了直接初始化形式

p1的初始化隱式的要求編譯器用一個new返回的int*來創建一個shared_ptr。由于我們不能進行內置指針到智能指針間的隱式轉換,因此這條初始化語句是錯誤的。出于相同的原因,一個返回shared_ptr的函數不能在其返回語句中隱式轉換一個普通指針。

shared_ptr<int> clone(int p) {return new int(p); //錯誤:隱式轉換為shared_ptr<int> }

必須將shared_ptr顯式綁定到一個想要返回的指針上。

shared_ptr<int> clone(int p) {return shared_ptr<int>(new int(p)); //正確:顯式地用int*創建shared_ptr<int> }

默認情況下:一個用來初始化智能指針的普通指針必須指向動態內存,因為智能指針默認使用delete釋放它所關聯的對象。

我們可以將智能指針綁定到一個指向其他類型資源的指針上,但是為了這樣做,必須提供自己的操作來替代delete。

定義和改變shared_ptr的其他方法

shared_ptr<T> p(q); //p管理內置指針q所指向的對象;q必須指向new分配的內存,//且能夠轉換為T*類型shared_ptr<T> p(u); //p從unique_ptr u 那里接管了對象的所有權;將u置空shared_ptr<T> p(q, d); //p接管了內置指針q所指向的對象的所有權,q必須能夠轉換為T*//類型。p將使用可調用對象d來代替deletep.reset(); //若p是唯一指向其對象的shared_ptr,reset會釋放此對象 p.reset(q); //若傳遞了可選的參數內置指針q,會令p指向q,否則將p置為空 p.reset(q, d); //若還傳遞了參數d,將會調用d而不是delete來釋放q

不要混合使用普通指針和智能指針

shared_ptr可以協調對象的析構,但這僅限于自身的拷貝(也是shared_ptr)之間。這也是為什么我們推薦使用make_shared而不是new的原因,這樣,就能在分配對象的同時就將shared_ptr與之綁定,從而避免了無意中將同一塊內存綁定到多個獨立創建的shared_ptr上。

//在函數被調用時,ptr被創建并初始化 void process(shared_ptr<int> ptr) {//使用ptr } //ptr離開作用域,被銷毀

process的參數是傳值方式傳遞的,因此實參會被拷貝到ptr中,拷貝一個shared_ptr會遞增其中的引用次數,因此,在process運行過程中,引用計數值至少為2。當process結束時,ptr的引用計數會遞減,但不會變為0。因此,當局部變量ptr被銷毀時,ptr指向的內存不會被釋放。

使用此函數的正確方法是傳遞給它一個shared_ptr:

shared_ptr<int> p(new int(42)); //引用計數為1 process(p); //拷貝p會遞增它的引用次數,在process中引用次數為2 int i = *p; //正確,引用次數為1

雖然不能傳遞給process一個內置指針,但是可以傳遞給它一個(臨時的)shared_ptr,這個shared_ptr是用一個內置指針顯式構造的。但是,這樣做很可能會導致錯誤:

int *x(new int(1024)); //危險:x是一個普通指針,不是一個智能指針 process(x); //錯誤。不能將int*轉換為shared_ptr<int> process(shared_ptr<int>(x)); //合法的,但內存會被釋放 int j = *x; //為定義的,x是一個空懸指針

在上面的調用中,我們將一個臨時的shared_ptr傳遞給process。當這個調用所在的表達式結束時,這個臨時對象就被銷毀了。銷毀這個臨時變量會遞減引用計數,此時引用計數就變為0了。因此,當臨時對象被銷毀時,它所指向的內存會被釋放。

但是x繼續指向(已經釋放的)內存,從而變成一個空懸指針。如果試圖使用x的值,其行為是為定義的。

當將一個shared_ptr綁定到一個普通指針時,我們就將內存的管理責任交給了這個shared_ptr。一旦這樣做了,我們就不應該再使用內置指針來訪問shared_ptr所指向的內存。

不要使用get初始化另一個智能指針或為智能指針賦值

智能指針類型定義了一個名為get的函數,它返回一個內置指針,指向智能指針管理的對象。但我們需要向不能使用智能指針的代碼傳遞一個內置指針時,使用get返回的指針的代碼不能delete此指針。

不能將get返回的指針綁定到智能指針上,這是錯誤的。

shared_ptr<int> p(new int(42)); //引用計數為1 int* q = p.get(); //正確:但使用q時要注意,不要讓它管理的指針被釋放 {//新程序塊shared_ptr<int>(q); //兩個獨立的shared_ptr指向相同的內存 } //程序塊結束,q被銷毀,它指向的內存被釋放 int foo = *p; //未定義:p指向的內存已經被釋放了

p和q指向相同的內存。由于它們是相互獨立創建的,因此各自的引用計數都是1。當q所在的程序塊結束時,q被銷毀,這回導致q指向的內存被釋放。從而p變成一個空懸指針,意味著當我們試圖使用p時,將發生未定義的行為。而且,當p被銷毀時,這塊內存會被第二次delete。

智能指針陷阱

  • 不使用相同的內置指針值初始化(或reset)多個智能指針
  • 不delete get()返回的指針
  • 不使用get()初始化或reset另一個智能指針
  • 如果你使用了get()返回的指針,記住當最后一個對應的智能指針銷毀時,你的指針就變為無效了。
  • 如果你使用智能指針管理的資源不是new分配的內存,記住傳遞給它一個刪除器。

unique_ptr

一個unique_ptr擁有它所指向的對象。與shared_ptr不同,某個時刻只能有一個unique_ptr指向一個給定對象。當unique_ptr被銷毀時,它所指向的對象也被銷毀。

與shared_ptr不同,沒有類似make_shared的標準庫函數返回一個unique_ptr。當我們定義一個unique_ptr時,需要將其綁定到一個new返回的指針上,初始化unique_ptr必須采用直接初始化形式。

unique_ptr<double> p1; //可以指向一個double的unique_ptr unique_ptr<int> p2(new int(42)); //p2指向一個值為42的int

由于一個unique_ptr擁有它指向的對象,因此unique_ptr不支持普通的拷貝或賦值操作:

unique_ptr<string> p1(new string("Stegosaurus")); unique_ptr<string> p2(p1); //錯誤:unique_ptr不支持拷貝 unique_ptr<string> p3; p3 = p2; //錯誤:unique_ptr不支持賦值

unique_ptr操作

unique_ptr<T> u1; //空unique_ptr,可以指向類型為T的對象。u1會使用delete unique_ptr<T, D> u2; //來釋放它的指針;u2會使用一個類型為D的可調用對象來釋放//它的指針unique_ptr<T, D> u(d); //空unique_ptr,指向類型為T的對象,用類型為D的對象d代替//deleteu.release(); //u放棄對指針的控制權,返回指針,并將u置為空u.reset(); //釋放u指向的對象 u.reset(q); //如果提供了內置指針q,令u指向這個對象,否則將u置為空 u.reset(nullptr);

雖然我們不能拷貝或賦值unique_ptr,但是可以通過調用release或reset將指針的所有權從一個(非const)unique_ptr轉移給另一個unique:

unique_ptr<string> p2(p1.release()); //將所有權從p1轉給p2 unique_ptr<string> p3(new string("Trex")); //將所有權從p3轉移給p2 p2.reset(p3.release()); //reset釋放了p2原來指向的內存

release成員返回unique_ptr當前保存的指針并將其置為空。因此,p2被初始化為p1原來保存的指針,而p1被置為空。

reset成員接受一個可選的指針參數,令unique_ptr重新指向給定的指針。如果unique_ptr不為空,它原來指向的對象被釋放。因此,對p2調用reset釋放了初始化string所使用的內存,將p3對指針的所有權轉移給p2,并將p3置為空。

調用release會切斷unique_ptr和它原來管理的對象間的聯系。release返回的指針通常被用來初始化另一個智能指針或給另一個智能指針賦值。如果我們不用另一個智能指針來保存release返回的指針,我們的程序就要負責資源的釋放。

p2.release(); //錯誤:p2不會釋放內存,而且我們丟失了指針 auto p = p2.release(); //正確,但我們必須記得delete(p)

不能拷貝unique_ptr的規則有一個例外:我們可以拷貝或賦值一個將要被銷毀的unique_ptr。

最常見的例子是從函數返回一個unique_ptr:

unique_ptr<int> clone(int p) {//正確,從int*創建一個unique_ptr<int>return unique_ptr<int>(new int(p)); }

還可以返回一個局部對象的拷貝:

unique_ptr<int> clone(int p) {unique_ptr<int> ret(new int(p));return ret; }

借助std::move()可以實現將一個unique_ptr對象賦值給另一個unique_ptr對象,其目的是實現所有權的轉移。

//A作為一個類 std::unique_ptr<A> ptr1(new A()); std::unique_ptr<A> ptr2 = std::move(ptr1);

weak_ptr

weak_ptr是一種不控制所指向對象生存期的智能指針。它指向由一個shared_ptr管理的對象。將一個weak_ptr綁定到一個shared_ptr不會改變shared_ptr的引用次數。一旦最后一個指向對象的shared_ptr被撤銷,對象就會被釋放。即使有weak_ptr指向對象,對象也還是會被釋放。

weak_ptr

weak_ptr<T> w; //空weak_ptr可以指向類型為T的對象weak_ptr<T> w(sp); //與shared_ptr sp 指向相同對象的weak_ptr。T必須//能夠轉換為sp指向的類型w = p; //p可以是一個shared_ptr或一個weak_ptr。賦值后//w與p共享對象w.reset(); //將w置空 w.use_count(); //與w共享對象的shared_ptr的數量 w.expired(); //若w.use_count()為0,返回true,否則返回false w.lock(); //如果expired為true,返回一個空shared_ptr;否則返回一個//指向w的對象的shared_ptr

當我們創建一個weak_ptr時,要用一個shared_ptr來初始化它

auto p = make_shared<int>(42); weak_ptr<int> wp(p); //wp弱共享p;p的引用計數未改變

本例中,wp和p指向相同的對象。由于是弱引用,創建wp不會改變p的引用計數;wp指向的對象可能被釋放掉。

由于對象可能不存在,我們不能使用weak_ptr直接訪問對象,而必須調用lock。此函數檢查weak_ptr指向的對象是否仍存在。如果存在,lock返回一個指向共享對象的shared_ptr。
由于weak_ptr并沒有重載operator->和operator *操作符,因此不可以直接通過weak_ptr使用對象,同時也沒有提供get()函數直接獲取裸指針。典型的用法是調用其lock函數來獲得shared_ptr示例,進而訪問原始對象。

初始化方式

  • 通過shared_ptr直接構造,也可以通過隱式轉換來構造;
  • 允許移動構造,也允許拷貝構造
#include <iostream> #include <memory>class Frame {}; int main() {std::shared_ptr<Frame> f(new Frame());std::weak_ptr<Frame> f1(f); //shared_ptr直接構造std::weak_ptr<Frame> f2 = f; //隱式轉換std::weak_ptr<Frame> f3(f1); //拷貝構造函數std::weak_ptr<Frame> f4 = f1; //拷貝構造函數std::weak_ptr<Frame> f5;f5 = f; //拷貝賦值函數f5 = f2; //拷貝賦值函數std::cout << f.use_count() << std::endl; //1 }

總結

以上是生活随笔為你收集整理的智能指针知识的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。