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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

智能指针的相关讲解

發布時間:2023/12/18 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 智能指针的相关讲解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • 1.new和delete操作符
      • 1)new運算符做了兩件事
      • 2)delete也做了兩件事
      • 3)補充:
    • 2.shared_ptr
      • 1)概念
      • 2)一般形式
      • 3)常規初始化(shared_ptr和new配合使用)
      • 4)make_shared函數
      • 5)shared_ptr引用計數的增加和減少
      • 6)shared_ptr指針常用操作
        • 1.use_count函數
        • 2.unique成員函數
        • 3.reset成員函數
        • 4.*解引用
        • 5.get成員函數
        • 6.swap成員函數
        • 7.=nullptr
        • 8.智能指針名字作為判斷條件
        • 9.指定刪除器和數組問題
    • 3.weak_ptr
      • 1)簡介
      • 2)常用操作
        • 1.use_count函數
        • 2.expired函數
        • 3.reset函數
        • 4.lock函數
      • 3)尺寸問題
    • 4.shared_ptr使用場景、陷阱、性能分析與使用建議
      • 1)std::shared_ptr使用場景
      • 2)std::shared_ptr使用陷阱分析
        • 1.慎用裸指針
        • 2.慎用get返回的指針
        • 3.用enable_shared_from_this返回this
        • 4.避免循環引用
      • 3)性能分析
        • 1.尺寸問題
        • 2.移動語義
      • 4)補充說明和使用建議
    • 5.unique_ptr簡介與常用操作
      • 1)unique_ptr簡介
      • 2)unique_ptr常用操作
        • 1.unique_ptr不支持的操作
        • 2.移動語義
        • 3.release成員函數
        • 4.reset成員函數
        • 5.=nullptr
        • 6.指向一個數組
        • 7.get成員函數
        • 8.*解引用
        • 9.swap成員函數
        • 10.智能指針名字作為判斷條件
        • 11.轉換成shared_ptr類型
      • 3)返回unique_ptr
      • 4)刪除器
      • 5)尺寸問題
    • 6.智能指針總結
      • 1)設計思想
      • 2)auto_ptr為什么被廢棄
      • 3)智能指針的選擇

1.new和delete操作符

1)new運算符做了兩件事

  • ①分配內存(new就是通過operate new來分配內存的)
  • ②調用構造函數初始化該內存

2)delete也做了兩件事

  • ①調用析構函數
  • ②釋放內存(delete就是通過operate delete()來釋放內存的)

3)補充:

delete [ ]pA中,C++會多分配4字節的大小專門專門保存數組的大小,在delete [ ] 時就可以去除這個數組大小的數字,就知道了需要調用析構函數多少次

2.shared_ptr

1)概念

  • 共享指針,多個指針指向同一個對象,最后一個指針被銷毀時,這個對象就會被釋放

2)一般形式

  • shared_ptr<指向的類型>智能指針名
  • shared_ptr<string.>p1; //這是一個指向string的智能指針,名字為p1

3)常規初始化(shared_ptr和new配合使用)

①常規

shared_ptr<int>pi(new int(100)); //pi指向一個值為100的int數據 shared_ptr<int>pi2 = new int(100);//這個寫法不行,智能指針必須是explicit,是不可以進行隱式類型轉換的,必須用直接初始化方式,而待等號一般都要表示隱式類型轉換

②對于返回值為shared_ptr<int.>類型,看看下面的范例:

shared_ptr<int> makes(int value) {return new int(value);//不可以,因為無法把new得到的int*換成shared_ptr }

所以要修改為

shared_ptr<int>makes(int value) {return shared_ptr<int>(new int(value));//可以,顯示使用int*創建shared_ptr<int> }

③裸指針可以用來初始化shared_ptr,但是這是一種不被推薦的用法,穿插使用容易出問題,盡量使用后面會講到的make_shared

int *pi = new int; shared_ptr<int> p1(pi);

上面的寫法不推薦,應該直接傳遞new運算符,而不是一個裸指針變量

shared_ptr<int>p1(new int);

4)make_shared函數

  • 簡介:被認為是最安全和高效的分配和使用shared_ptr智能指針的模板,能在動態內存(堆)中分配并初始化一個對象,然后返回指向此對象的shared_ptr
shared_ptr<int>p2 = std::make_shared<int>(100);//這個shared_ptr指向一個值為100的整型的內存,類似int *pi = new int(100); shared_ptr<string>p3 = std::make_shared<string>(5,'a');//5個字符,類似于string mystr(5,'a');注意到,make_shared后圓括號里的參數的形式取決于"<>"中的類型名,此時這些參數必須和string里的某個構造函數匹配 shared_ptr<int>p4 = make_shared<int>();//p4指向一個int,int里面保存的值是0,這個就是值初始化 p4 = make_shared<int>(400);//p4釋放剛才的對象,重新指向新對象 auto p5 = std::make_shared<string>(5,'a');//用auto保存make_shared結果,寫法簡單
  • make_shared使用起來雖然不錯,后面還提到自定義刪除器,如果使用make_shared方法生成shared_ptr對象,那就沒有辦法自定義刪除器了

5)shared_ptr引用計數的增加和減少

1.引用計數的增加
每個shared_ptr都會記錄有多少個其他shared_ptr指向相同的對象
(1)像下面的代碼這樣,p6初始化p7,就會導致所有指向該對象(內存)的shared_ptr引用計數全部增加1

auto p6 = std::make_shared<int>(100);//目前p6所指的對象只有p6一個引用者 auto p7(p6);//寫成auto p7 = p6;也可以,智能指針復制,p7和p6指向相同的對象,此對象有兩個引用者

(2)把引用計數當成實參往函數里面傳遞

void myfunc(shared<int>&ptmp)//傳遞引用作為形參,則引用計數不會增加 {return ptmp; }

在main主函數中,繼續增加如下代碼

myfunc(p7);//這個函數執行后,這個指針的引用計數會恢復

(3)作為函數的返回值

shared_ptr<int>myfunc2(shared_ptr<int>&ptmp)//這里是引用,所以計數還是2 {return ptmp; }

在主函數中增加以下代碼

auto p8 = myfunc2(p7);//p8接受myfunc2函數返回值,那么此時引用計數會變成3

2.引用計數的減少
(1)給shared_ptr賦一個新值,讓該shared_ptr指向一個新對象,在main主函數中增加以下代碼

p8 = std::make_shared<int>(200);//p8指向新對象1,p6,p7計數從3恢復為2 p7 = std::make_shared<int>(200);//p7指向新對象1,p6計數的源對象恢復計數為1 p6 = std::make_shared<int>(200);//p6指向新對象1,p6指向的原對象內存被釋放

(2)局部的shared_ptr離開作用域

auto p6 = std::make_shared<int>(100); auto p7(p6);// myfunc(p7);//進入函數體myfunc中時有3個引用計數,從myfunc中返回時引用計數恢復為2

(3)當一個shared_ptr引用計數為0,他會Zion給釋放自己所管理的對象

auto p9 = std::make_shared<int>(100);//只有p9指向該對象 auto p10 = std::make_shared<int>(100); p9 = p10;//p9指向p10的對象,該對象引用計數為2,而原來p9指向的對象引用計數會變為0,所以會被自動釋放

6)shared_ptr指針常用操作

1.use_count函數

用于返回多少個智能指針指向某個對象

shared_ptr<int>myp(new int(100)); int icount = myp.use_count();//1 shared_ptr<int>myp2(myp); icount = myp2.use_count();//2

2.unique成員函數

是否該智能指針獨占某個指向的對象,,也就是若只有一個智能指針指向某個對象,則unique返回true,否則返回false

shared_ptr<int>myp(new int(100)); if(myp.unique()) //本條件成立 {cout<<"myp unique ok"<<endl; } shared_ptr<int>myp2(myp); if(myp.unique()) {cout<<"myp unique ok"<<endl; }

3.reset成員函數

(1)當reset不帶參數時
當pi是唯一指向該對象的指針,則釋放pi所指向的對象,將pi置空
若pi不是唯一指向該對象的指針,則不釋放pi所指向的對象,但指向該對象引用計數會減1,同時將pi置空

shared_ptr<int>(new int(100)); pi.reset();//釋放Pi指向的對象,將pi置空 if(pi == nullptr)//條件成立 {cout<<"pi被置空"<<endl; }

繼續演示若pi不是唯一指向該對象的指針的情形

shared_ptr<int>(new int(100)); auto pi2(pi); //pi2引用計數現在為2 pi.reset(); //pi被置空,pi2引用計數變為1

(2)當reset帶參數(一般是一個new出來的指針)時
若pi是唯一指向該對象的指針,則釋放pi所指向的對象,讓pi指向新內存
若pi不是唯一指向該對象的指針,則不釋放pi指向對象,但是指向該對象的引用計數會減1,同時讓pi指向新內存

shared_ptr<int>pi(new int(100)); pi.reset(new int(1));//釋放原內存,指向新內存

演示若pi不是唯一指向該對象的指針的情形

shared_ptr<int>pi(new int(100)); auto pi2(pi); pi.reset(new int(1));//現在pi引用計數為1,pi2引用計數也為1 if(pi.unique())//本條件成立 {cout<<"pi unique ok"<<endl; }

(3)空指針也可以通過reset來重新初始化

shared_ptr<int>p; p.reset(new int(100));//p指向新內存

4.*解引用

獲得p指向的對象

shared_ptr<int>pother(new int(12345)); char outbuf[1024]; sprintf_s(outbuf,sizeof(outbuf),"%d",*pother);//outbuf中的內容就是12345,pother不發生變化,引用計數仍舊為1 OutputDebugStringA(outbuf);//在MyProjectMFC工程中使用F5運行,執行到這行可以打印輸出outbuf的內容

5.get成員函數

p.get()返回p中保存的指針
小心使用,若智能指針釋放了所指向的對象,則返回的這個指針所指向的對象就變得無效了

shared_ptr<int>myp(new int(100)); int *p = myp.get(); * p = 45;

6.swap成員函數

交換兩個智能指針所指向的對象

shared_ptr<string>ps1(new string("I love china1!")); shared_ptr<string>ps2(new string("I love china2!")); std::swap(ps1,ps2);//可以這么操作 ps1.swap(ps2);//也可以這么操作

7.=nullptr

  • 將指針指向的引用計數減1,若引用計數變為0,則釋放智能指針所指向的對象
  • 將智能指針置空
shared_ptr<string>ps1(new string("I love china!")); ps1 = nullptr;

8.智能指針名字作為判斷條件

shared_ptr<string> ps1(new string("I love china!")); //若ps1指向一個對象,則條件成立 if(ps1)//條件成立 {cout<<"ps1"<<endl;//執行 }

9.指定刪除器和數組問題

1)指定刪除器
可以為智能指針定義自己的寫的刪除器

void myDeleter(int *p)//自己的刪除器,刪除整型指針用的,當p的引用計數為0,則自動調用這個刪除器刪除對象,釋放內存 {delete p; }

在main主函數中,加入如下代碼:

shared_ptr<int>p(new int(12345),myDeleter);//指定刪除器 shared_ptr<int>p2(p); p2.reset();//p2為nullptr了 p.reset();//調用自己的刪除器,釋放鎖指向的對象,同時p置空

lamdba表達式也可以定義刪除器

shared_ptr<int>p(new int(12345),[](int*p) {delete p; } p.reset();//會帶哦用刪除器(lamdba表達式)

為什么要自己定義刪除器?

當默認的刪除器處理不了——用shared_ptr管理動態數組的時候,需要自己指定自己的刪除器,默認的刪除器不支持數組對象

shared_ptr<int[]>p(new int[10],[](int*p)) {delete[]p; }); p.reset();

如果一個類中帶有析構函數,那么必須定義自己的刪除器,否則會報異常

class A { public:A(){cout<<""<<endl;}~A(){cout<<""<<endl;} };

在main主函數中加入如下代碼

shared_ptr<A>pA(new A[10),[](A*p) {delete[]p; }); //還可以這么寫 shared_ptr<A>pA(new A[10],std::default_delete<A[]>()); //不寫刪除器,也可以這么定義 shared_ptr<A[]>pA(new A[]);//<>中加個[]就行 shared_ptr<int[]>p(new in[10]);

2)指定刪除器的額外說明

3.weak_ptr

1)簡介

  • 用來輔助shared_ptr工作的
  • 將weak_ptr綁定到shared_ptr并不會改變shared_ptr的引用計數(更確切的說,weak_ptr的構造和析構函數不會增加或減少鎖指向對象的引用計數)
  • weak_ptr的創建一般用make_shared來初始化
auto pi = make_shared<int>(100); weak_ptr<int>piw(p1);//piw弱共享pi,pi引用計數(強引用計數)不改變,弱引用計數會從0變成1,pi和piw指向相同位置 //也可以這么寫 weak_ptr<int>piw; piw = pi;//這里是一個shared_ptr,賦值給一個weak_ptr,pi和piw兩者指向相同位置
  • 程序員不能通過weak_ptr直接訪問對象的,必須要用一個lock的成員函數,lock的功能就是檢查weak_ptr鎖指向的對象是否還存在,如果存在,lock能夠返回一個空的shared_ptr
auto pi2 = piw.lock();//強引用(shared_ptr)計數會加1,現在pi是兩個強引用,兩個弱引用 if(pi2 != nullptr) {cout<<"所指對象存在"<<endl; }
  • weak_ptr具備能夠判斷所指向的對象是否存在的能力

2)常用操作

1.use_count函數

auto pi = make_shared<int>(100); auto pi2(pi);//pi2類型是一個shared_ptr weak_ptr<int>piw(pi); int isc = piw.use_count();

2.expired函數

  • 是否過期的意思,弱該指針的use_count為0,則返回true,否則返回false
pi.reset(); pi2.reset(); if(piw.expired())//如果過期 {cout<<"piw已經過期"<<endl; }

3.reset函數

  • 將該弱引用指針設置為空,不影響指向該對象的強引用數量,但指向該對象的弱引用數量會減1
auto pi = make_shared<int>(42); weak_ptr<int>piw(pi); piw.resset(); //pi是一個強引用,無弱引用

4.lock函數

  • 獲得監視的shared_ptr,下面是完整的演示
auto p1 = make_shared<int>(42); weak_ptr<int>pw; pw = p1;//用shared_ptr給weak_ptr值,現在p1是1個強引用1個弱引用 if(!pw.expired())//如果pw沒過期 {auto p2 = pw.lock();//現在p1是2個強引用1個弱引用if(p2 != nullptr){cout<<"所指對象存在"<<endl;} //離開這個范圍,p1的強引用計數恢復為1,弱引用計數保持為1 } else//若pw已經過期 {cout<<"pw已經過期"<<endl; }
  • 上面的代碼改造以下,看如下這個比較完整的演示,引入一個{ }
weak_ptr<int>pw; {auto p1 = make_shared<int>(42);pw = p1;//用shared_ptr給weak_ptr值 }//離開這里p1就都失效了 //這里pw這個weak_ptr就會過期了 if(pw.expired())//pw已經過期,進入if條件執行語句,打印 {cout<<"pw已經過期了"<<endl; }

3)尺寸問題

  • weak_ptr的尺寸是裸指針的2倍,其他略

4.shared_ptr使用場景、陷阱、性能分析與使用建議

1)std::shared_ptr使用場景

shared_ptr<int>create0(int value) {return make_shared<int>(value);//返回一個shared_ptr } void myfunc(int value) {shared_ptr<int>ptmp = create0(10);return;//ptmp離開了作用域(ptmp是局部變量),因此他指向的內存會被自動釋放 }
  • 在主函數中,加入如下代碼
    myfunc(12);
  • 現在改造以下myfunc函數
shared_ptr<int>myfunc(int value) {shared_ptr<int>ptmp = create0(10);return ptmp;//這個return會導致引用計數遞增,所以ptmp指向的內存不會釋放,者相當于返回了一個ptmp的復制,ptmp銷毀計數-1,return ptmp;使計數+1 } //主函數中,用一個變量接住返回的shared_ptr指針才會使計數+1 auto p11 = myfunc(12);

2)std::shared_ptr使用陷阱分析

1.慎用裸指針

  • 如果把一個普通裸指針綁定到了一個shared_ptr,那么內存管理的責任就交給智能指針,就不應該再使用裸指針(內置指針)訪問shared_ptr指定的內存了
shared_ptr<int>myp(new int(100)); proc(myp); *myp = 45;//myp可是shared_ptr<int>類型,*表示解引用
  • 但是不要用裸指針初始化多個shared_ptr對象,兩個指針無關聯關系,釋放裸指針所指向的內存要釋放2次,這顯然會出問題
int * pi = new int; shared_ptr<int>p1(pi); shared_ptr<int>p2(pi);

修改為

//可修改為 shared_ptr<int>p1(new int);//大大降低了用pi來創建p2的可能性

2.慎用get返回的指針

  • get返回的指針不能delete,否則會產生異常,也不能將其他智能指針綁到get返回的指針上

3.用enable_shared_from_this返回this

  • 看如下代碼
class CT { public:shared_ptr<CT>getself(){return shared_ptr<CT>(this);} };
  • 在main主函數里面,加入如下代碼
shared_ptr<CT>pct1(new CT); shared_ptr<CT>pct2 = pct1;//沒問題,2個強引用 //第二句若改成 shared_ptr<CT>pct2 = pct1->getself();//問題出現
  • 上面的代碼用同一個指針構造了兩個智能指針pct1和pct2,兩個之怎能指針之間沒有任何關系,也就是釋放同一個都西昂內存會釋放兩次,解決方法如下
class CT :public std::enable_shared_from_this<CT>//C++標準庫提供的類模板 { public:shared_ptr<CT>getself(){return shared_ptr_from_this();//通過這個方法返回智能指針} }; //主函數代碼不變 shared_ptr<CT>pct1(new CT); shared_ptr<CT>pct2 = pct1->getself();

4.避免循環引用

  • 循環引用會導致內存泄漏
  • 解決辦法:把其中一個shared_ptr寫成weak_ptr
class CB { public:/shared_ptr<CA> m_pas;weak_ptr<CA> m_pas;~CB(){cout<<"~B()執行了"<<endl;} }; //主函數調用 shared_ptr<CA>pca(new CA); shared_ptr<CB>pcb(new CB); pca->m_pbs = pcb;//現在等價于指向CB對象的有兩個強引用 pcb->m_pas = pca;//因為m_pas是弱引用,所以指向CA對象的只有一個強引用,離開作用域后,先執行CA的析構函數,后執行CB的析構函數

3)性能分析

1.尺寸問題

尺寸是裸指針的2倍

2.移動語義

  • 全程引用計數為1
shared_ptr<int>p1(new int(100));//p1指向該對象(內存) shared_ptr<int>p2(std::move(p1));//移動語義 shared_ptr<int>p3; p3 = std::move(p2);

4)補充說明和使用建議

make_shared比普通指針的智能效率高,只分配一次內存

shared_ptr<string>ps1(new string("I love China!"));//這句話至少分配兩次內存

5.unique_ptr簡介與常用操作

1)unique_ptr簡介

  • 獨占式智能指針
  • unique的一般形式
unique_ptr<指向的對象類型>智能指針變量名

1.常規初始化

unique_ptr<int>pi2(new int(102));

2.make_unique函數

  • C++11不支持,C++14才有
unique_ptr<int>p1 = std::make_unique<int>(100); auto p2 = std::make_unique<int>(200); //若不用make_unique,就得像上面常規初始化那樣寫

2)unique_ptr常用操作

1.unique_ptr不支持的操作

  • 不支持復制和賦值
unique_ptr<string>ps1(new string("I love China!")); unique_ptr<string>ps2(ps1);//不可以,不支持復制 unique_ptr<string>ps4; ps4 = ps1;//不可以,不支持賦值操作

2.移動語義

  • 支持移動語義
unique_ptr<string>ps1(new string("I love China!")); unique_ptr<string>ps2 = std::move(ps1);//ps1空了,ps3指向ps1原先所指

3.release成員函數

  • 放棄對指針的控制權,返回裸指針,將智能指針放空。返回的裸指針可以手工delete釋放,也可以用來初始化另外一個智能指針,或者給另外一個智能指針賦值
/將所有權從ps1轉移(移動)給ps2 unique_ptr<string>ps1(new string("I love China!")); unique_ptr<string>ps2(ps1.release()); if(ps1 == nullptr) //條件被置空 {cout<<"ps1被置空"<<endl; } /下面這個語句,內存會泄漏 ps2.release(); /所以要這么修改 string *tempp = ps2.release(); 或者是auto tempp = ps.release() delete tempp;

4.reset成員函數

和shared_ptr一樣

5.=nullptr

和shared_ptr一樣

6.指向一個數組

std::unique_ptr<int[]>ptrarray(new int[10]); ptrarray[0] = 12;//數組提供索引運算符[] ptrarray[2] = 9;

7.get成員函數

和shared_ptr一樣

8.*解引用

  • 數組是沒有*解引用運算符的
unique_ptr<string>(new string("I love China!")); const char *p1 = ps1->c_str(); *ps1 = "This is a test !"; const char*p2 = ps1->c_str();//p1和p2是不同的內存地址,string內部機制決定的

9.swap成員函數

和shared_ptr一樣

10.智能指針名字作為判斷條件

若ps1指向一個對象,則不為空

11.轉換成shared_ptr類型

unique_ptr<std::string>ps(new std::string("I love China!")); shared_ptr<string>ps2 = std::move(ps);

3)返回unique_ptr

  • 生成局部對象的unique_ptr可以返回復制,因為要被銷毀了

4)刪除器

1.指定刪除器
1)范例

void mydeleter(string *pdel) {delete pdel;pdel = nullptr; } /主函數加入 typdef void(*fp)(string*);//定義一個函數指針,類型名為fp unique_ptr<string,fp>ps1(new string("I love China!"),mydeleter);

2.補充指定刪除器
shared_ptr的刪除器更靈活,相同類型就可以共用刪除器,但是unique_ptr的刪除器不一樣,不靈活

5)尺寸問題

通常情況下unique_ptr的尺寸和裸指針一樣,若刪除器是一個函數,unique_ptr的尺寸就會發生變化

6.智能指針總結

1)設計思想

防止忘記內存釋放,造成內存泄漏

2)auto_ptr為什么被廢棄

不能在容器中保存auto_ptr,也不能從函數中返回auto_ptr,已經被unique_ptr取代

3)智能指針的選擇

優先考慮unique_ptr,要使用多個指向同一個對象的指針的話用shared_ptr

總結

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

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