一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

C++內(nèi)存管理之shared

 wuxinit_ 2020-12-16

 ----------------------------------------shared_ptr---------------------------------------

 

引子

  c++中動(dòng)態(tài)內(nèi)存的管理是通過(guò)new和delete來(lái)完成的,只要保證new和delete的配對(duì)使用,是沒(méi)有問(wèn)題的。但是有時(shí)候我們會(huì)忘記釋放內(nèi)存,甚至有時(shí)候我們根本就不知道什么時(shí)候釋放內(nèi)存。特別時(shí)在多個(gè)線程間共享數(shù)據(jù)時(shí),更難判斷內(nèi)存該何使釋放。這種情況下就機(jī)器容易產(chǎn)生引用非法內(nèi)存的指針?!                                                                                                   ?/span>

  為了更容易(同時(shí)也更安全的管)的使用動(dòng)態(tài)內(nèi)存,新的標(biāo)準(zhǔn)庫(kù)(C++11)提供了兩種智能指針(smart pointer)類型來(lái)管理動(dòng)態(tài)對(duì)象。智能指針的行為類似于常規(guī)指針。重要的區(qū)別是它負(fù)責(zé)自動(dòng)釋放所指向的對(duì)象。新標(biāo)準(zhǔn)提供的這兩種智能指針的區(qū)別在于管理底層指針的方式:shared_ptr允許多個(gè)指針指向同一個(gè)對(duì)象;unique_ptr則獨(dú)占所指向的對(duì)象。標(biāo)準(zhǔn)庫(kù)還定義了一個(gè)weak_ptr的伴隨類,他是一種弱引用,指向shared_ptr所管理的對(duì)象。這三種類型都定義在memory頭文件中。

 

 

 

 

 

 

 

 

 初始化 sahred_ptr

  智能指針的使用方式與普通指針類似。解引用一個(gè)智能指針?lè)祷厮赶虻膶?duì)象。如果在一個(gè)條件判斷中使用智能指針,效果就是檢測(cè)它是否為空:

 

復(fù)制代碼
#include <iostream>

using namespace std;

int main()
{  
  /*---------空指針------------*/
   shared_ptr<string> p1;
    if(!p1)                         //!默認(rèn)初始化的智能指針中保存著一個(gè)空指針!并不是""空字符串
        cout<<"p1==NULL"<<endl;

  /*---------初始化------------*/
  shared_ptr<string> p2(new string); 
  if(p2&&p2->empty()){         //!需要注意的時(shí)empty時(shí)屬于string的成員函數(shù)。
    *p2="helloworld";
    cout<<*p2<<endl;
  }
//    shared_ptr<int> pa = new int(1);//!error:不允許以暴露裸漏的指針進(jìn)行賦值操作。

  //一般的初始化方式 shared_ptr<string> pint(new string("normal usage!")); cout<<*pint<<endl; //推薦的安全的初始化方式 shared_ptr<string> pint1 = make_shared<string>("safe uage!"); cout<<*pint1<<endl;
}
復(fù)制代碼

 

 關(guān)于其它初始化智能指針的方法,如下;不推薦!在這里展示出來(lái)是希望極力避免不安全的使用范例。

復(fù)制代碼
    /*不推薦*/
    int * p = new int(32);
    shared_ptr<int> pp(p);
    cout<<*pp<<endl;

    /*意外的情況*/
//    delete p;               //!不小心把delete掉了。
//    cout<<*pp<<endl;·       //!pp也不再有效。
復(fù)制代碼

 

 

關(guān)于get()函數(shù);

智能指針定義了一個(gè)名為get的函數(shù),它返回一個(gè)內(nèi)置指針,指向智能指針的管理的對(duì)象。此函數(shù)設(shè)置的初衷是當(dāng)我們向不能使用智能指針的代碼傳遞一個(gè)內(nèi)置指針。使用get返回指針的代碼不能delete此指針。

復(fù)制代碼
#include <iostream>
#include <memory>
using namespace std;

void useShared_ptr(int *p)
{
    cout<<*p<<endl;
}

void delePointer(int *p)
{
    delete p;
}

int main(int argc, char *argv[])
{
    shared_ptr<int> p1 = make_shared<int>(32);
//    shared_ptr<int>p2(p1.get());  //!錯(cuò)誤的用法:但是p1、p2各自保留了對(duì)一段內(nèi)存的引用計(jì)數(shù),其中有一個(gè)引用計(jì)數(shù)耗盡,資源也就釋放了。
    useShared_ptr(p1.get());
//    delePointer(p1.get());        //!error:
    return 0;
}
復(fù)制代碼

 再次聲明:get用來(lái)將指針的訪問(wèn)權(quán)限傳遞給代碼,只有在確定代碼不會(huì)delete指針的情況下,才能使用get。特別是,永遠(yuǎn)不要用get初始化另一個(gè)智能指針或者為另一個(gè)智能指針賦值!

 

 

 關(guān)于mak_shared函數(shù):

  最安全的分配和使用動(dòng)態(tài)內(nèi)存的方法是調(diào)用一個(gè)名為make_shared的標(biāo)準(zhǔn)庫(kù)函數(shù),此函數(shù)在動(dòng)態(tài)內(nèi)存中分配一個(gè)對(duì)象并初始化它,返回此對(duì)象的shared_ptr。與只能指針一樣,make_shared也定義在頭文件memory中。

 

復(fù)制代碼

#include <iostream>
using namespace std;

int main()
{
    shared_ptr<int> p3 = make_shared<int>(42);
    cout<<*p3<<endl;

    shared_ptr<string> pstr = make_shared<string>("99999");
    cout<<*pstr<<endl;

    shared_ptr<int> pint = make_shared<int>(); //!默認(rèn)初始化為 0
    cout<<*pint<<endl;

    auto pau = make_shared<string>("auto");    //!更簡(jiǎn)單,更常用的方式。
    cout<<*pau<<endl;
}
復(fù)制代碼

  使用make_shared用其參數(shù)來(lái)構(gòu)造給定類型的對(duì)象;傳遞的參數(shù)必須能夠與該類型的某個(gè)構(gòu)造函數(shù)相匹配。

  通常我們用auto來(lái)定義一個(gè)對(duì)象來(lái)保存make_shared的結(jié)果,這種方式更為簡(jiǎn)單。

 

shared_ptr的拷貝和賦值

  當(dāng)進(jìn)行拷貝或者賦值操作時(shí),每個(gè)shared_ptr都會(huì)記錄有多少個(gè)其他的shared_ptr指向相同的對(duì)象:

復(fù)制代碼

#include <iostream>
using namespace std;

int main()
{
    auto p = make_shared<int>(42); //!p指向的對(duì)象只有p一個(gè)引用者。
    cout<<p.use_count()<<endl;
    auto q(p);                     //!p和q指向相同的對(duì)象,此對(duì)象有兩個(gè)引用者。
    cout<<p.use_count()<<endl;
    return 0;
}
復(fù)制代碼

 

 

shared_ptr作返回值:

復(fù)制代碼

#include <iostream>
using namespace std;

shared_ptr<string> factory(const char* p){
    return make_shared<string>(p);
}

void use_factory(){
    shared_ptr<string> p = factory("helloworld"); 
    cout<<*p<<endl;          //!離開(kāi)作用域時(shí),p引用的對(duì)象被銷毀。

}
shared_ptr<string> return_share_ptr()
{
  shared_ptr<string> p = factory("helloworld");
  cout<<*p<<endl;
  return p;               //!返回p時(shí),引用計(jì)數(shù)進(jìn)行了遞增操作。
}                      //!p離開(kāi)了作用域,但他指向的內(nèi)存不會(huì)被釋放掉。




int main()
{
  use_factory();
  auto p = return_share_ptr();
  cout<<p.use_count()<<endl;
}
復(fù)制代碼

 

 

引用計(jì)數(shù):

  可以認(rèn)為每個(gè)shared_ptr都有一個(gè)關(guān)聯(lián)的計(jì)數(shù)器,通常稱其為引用計(jì)數(shù)。無(wú)論何時(shí)我們拷貝一個(gè)shared_ptr,計(jì)數(shù)器都會(huì)遞增。例如,當(dāng)用一個(gè)shared_ptr去初始化另一個(gè)shared_ptr;當(dāng)我們給shared_ptr賦予一個(gè)新的值或者是shared_ptr被銷毀(例如一個(gè)局部的shared_ptr離開(kāi)其作用域)時(shí),計(jì)數(shù)器就會(huì)遞減。一旦一個(gè)shared_ptr的計(jì)數(shù)器變?yōu)?,他就會(huì)自動(dòng)釋放自己所管理的對(duì)象。

復(fù)制代碼
#include <iostream>
using
namespace std; int main() { auto p = make_shared<int>(42); //!指向的對(duì)象只有p一個(gè)引用者。 cout<<p.use_count()<<endl; auto q = make_shared<int>(56);//!指向的對(duì)象只有q一個(gè)引用者。 cout<<q.use_count()<<endl; cout<<"---------afterAssin-----"<<endl; p = q; //!p原來(lái)引用的對(duì)象經(jīng)過(guò)賦值之后釋放掉了,q引用的對(duì)象有了p和q兩個(gè)引用。 cout<<*p<<"=="<<*q<<endl; cout<<q.use_count()<<endl; }
復(fù)制代碼

 

其他shared_ptr操作

  shared_ptr還定義了一些其他的操作,參考前面的shared_ptr操作表格,例如,我們可以用reset將一個(gè) 新的指針賦予一個(gè)shared_ptr:

 

復(fù)制代碼
#include <iostream>
#include <memory>
using namespace std;

int main()
{
    shared_ptr<string> p1(new string("helloworld--1"));
//    p1 = new string("helloworld2--2");//error!
        p1.reset(new string("helloworld2--2"));
    cout<<*p1<<endl;
}
復(fù)制代碼

 

  與賦值類似,reset會(huì)更新(-1)引用計(jì)數(shù),如果需要的話,會(huì)釋放p1指向的對(duì)象。reset成員經(jīng)常與unique一起使用,來(lái)控制多個(gè)shared_ptr的共享對(duì)象。在改變底層對(duì)象之前,我們?cè)跈z查自己是否是當(dāng)前對(duì)象僅有的用戶。如果不是,在改變之前要做一份新的拷貝:

復(fù)制代碼
#include <iostream>
#include <memory>
using namespace std;

int main()
{
    shared_ptr<string> p1(new string("helloworld--1"));
    shared_ptr<string> p2(p1);

    if(p1.unique())
        cout<<*p1 + string(" is unique!")<<endl;
    else{
        p1.reset(new string("new reset!"));
        cout<<*p1<<endl;
    }
}
復(fù)制代碼

 

 

 

 

容器中的shared_ptr-記得用erease節(jié)省內(nèi)存 

  對(duì)于一塊內(nèi)存,shared_ptr類保證只要有任何shared_ptr對(duì)象引用它,他就不會(huì)被釋放掉。由于這個(gè)特性,保證shared_ptr在不用之后不再保留就非常重要了,通常這個(gè)過(guò)程能夠自動(dòng)執(zhí)行而不需要人工干預(yù),有一種例外就是我們將shared_ptr放在了容器中。所以永遠(yuǎn)不要忘記erease不用的shared_ptr。

復(fù)制代碼

#include <iostream>
using namespace std;
int main()
{

    list<shared_ptr<string>>pstrList;
    pstrList.push_back(make_shared<string>("1111"));
    pstrList.push_back(make_shared<string>("2222"));
    pstrList.push_back(make_shared<string>("3333"));
    pstrList.push_back(make_shared<string>("4444"));

    for(auto p:pstrList)
    {
        if(*p == "3333");
        {
            /*do some thing!*/
        }
        cout<<*p<<endl;
    }

    /*包含"3333"的數(shù)據(jù)我們已經(jīng)使用完了!*/
    list<shared_ptr<string>>::iterator itr = pstrList.begin();

    for(;itr!=pstrList.end();++itr)
    {
        if(**itr == "3333"){
            cout<<**itr<<endl;
            pstrList.erase(itr);
        }
    }

    cout<<"-------------after remove------------"<<endl;
    for(auto p:pstrList)
    {
        cout<<*p<<endl;
    }

  while(1)
  {
    /*do somthing other works!*/
    /*遍歷 pstrList*/ //!這樣不僅節(jié)約了大量?jī)?nèi)存,也為容器的使用增加了效率
  
  }
 }
復(fù)制代碼

 

狀態(tài)共享——why use shared_ptr?

  使用shared_ptr在一個(gè)常見(jiàn)的原因是允許多個(gè)多個(gè)對(duì)象共享相同的狀態(tài),而非多個(gè)對(duì)象獨(dú)立的拷貝!

復(fù)制代碼
#include <iostream>

using namespace std;

void copyCase()
{
    list<string> v1({"1","b","d"});
    list<string> v2 = v1;        //!v1==v2占用兩段內(nèi)存

    v1.push_back("cc");            //!v1!=v2

    for(auto &p:v1){
        cout<<p<<endl;
    }
    cout<<"--------void copyCase()---------"<<endl;
    for(auto &p:v2){
        cout<<p<<endl;
    }
} //v1和v2分屬兩個(gè)不同的對(duì)象,一個(gè)改變不會(huì)影響的狀態(tài)。

void shareCase()
{
    shared_ptr<list<string>> v1 = make_shared<list<string>>(2,"bb");
    shared_ptr<list<string>> v2 = v1;

    (*v1).push_back("c2c");
    for(auto &p:*v1){
        cout<<p<<endl;
    }
    cout<<"----------shareCase()--------"<<endl;
    for(auto &p:*v2){
        cout<<p<<endl;
    }
} //v1和v2屬于一個(gè)對(duì)象的兩個(gè)引用,有引用計(jì)數(shù)為證,其內(nèi)容的改變是統(tǒng)一的。

int main()
{
    copyCase();
    cout<<"++++++++++++++++"<<endl;
    shareCase();
}
復(fù)制代碼

 

 

智能指針與異常

 

  異常發(fā)生后,常規(guī)的動(dòng)態(tài)內(nèi)存常常不能正確釋放。但是如果使用智能指針,即程序過(guò)早結(jié)束,智能指針也能確保在內(nèi)存不需要時(shí)將其釋放:

 

void f()
{
     shared_ptr<int>sp(new int(42)) ; 
}

 

  函數(shù)的推出,要么有兩種情況,正常處理結(jié)束或者發(fā)生了異常,無(wú)論哪種情況,局部對(duì)象都會(huì)被銷毀。在上面的程序中,sp是一個(gè)shared_ptr,因此sp銷毀時(shí)會(huì)檢查引用計(jì)數(shù)。在此例中,sp是指向這塊內(nèi)存的唯一指針。所以會(huì)被釋放掉。

 

  與之相對(duì)的,當(dāng)發(fā)生異常時(shí),我們直接管理的內(nèi)存時(shí)不會(huì)自動(dòng)釋放的,如果使用內(nèi)置指針管理內(nèi)存,且在new之后對(duì)應(yīng)的delet之前發(fā)生異常,則內(nèi)存不會(huì)釋放。

 

void f()
{
    int *p = new int(42);
    //code//!異常拋出,且沒(méi)有在f()中被捕獲。
    delete p;      
}

 

   如果在new和delete之間發(fā)生異常,且異常未在f()中捕獲,則內(nèi)存就永遠(yuǎn)不會(huì)被釋放了。

 

 

 

 

shared_ptr對(duì)象的銷毀

1)管理動(dòng)態(tài)數(shù)組

  默認(rèn)情況下,shared_ptr指向的動(dòng)態(tài)的內(nèi)存是使用delete來(lái)刪除的。這和我們手動(dòng)去調(diào)用delete然后調(diào)用對(duì)象內(nèi)部的析構(gòu)函數(shù)是一樣的。與unique_ptr不同,shared_ptr不直接管理動(dòng)態(tài)數(shù)組。如果希望使用shared_ptr管理一個(gè)動(dòng)態(tài)數(shù)組,必須提供自定義的刪除器來(lái)替代delete 。

 

復(fù)制代碼
#include <iostream>
using
namespace std; class DelTest { public: DelTest(){ j= 0; cout<<" DelTest()"<<":"<<i++<<endl; } ~DelTest(){ i = 0; cout<<"~ DelTest()"<<":"<<i++<<endl; }
  static int i,j; };

int DelTest::i = 0;
int DelTest::j = 0; void noDefine() { cout<<"no_define start running!"<<endl; shared_ptr<DelTest> p(new DelTest[10]); } void slefDefine() { cout<<"slefDefine start running!"<<endl; shared_ptr<DelTest> p(new DelTest[10],[](DelTest *p){delete[] p;}); }                 //!傳入lambada表達(dá)式代替delete操作。 int main() { noDefine();   //!構(gòu)造10次,析構(gòu)1次。內(nèi)存泄漏。 cout<<"----------------------"<<endl; slefDefine(); //!構(gòu)造次數(shù)==析構(gòu)次數(shù) 無(wú)內(nèi)存泄漏 }
復(fù)制代碼

  通過(guò)自定義刪除器的方式shared_ptr雖然管理的是一個(gè)動(dòng)態(tài)數(shù)組。但是shard_ptr并不支持下標(biāo)運(yùn)算符的操作。而且智能指針類型不支持指針?biāo)阈g(shù)運(yùn)算(不能取地址)。因此為了訪問(wèn)數(shù)組中的元素,必須用get獲取一個(gè)內(nèi)置指針,然后用它來(lái)訪問(wèn)數(shù)組元素。

 

2)管理非常規(guī)動(dòng)態(tài)對(duì)象

  某些情況下,有些動(dòng)態(tài)內(nèi)存也不是我們new出來(lái)的,如果要用shared_ptr管理這種動(dòng)態(tài)內(nèi)存,也要自定義刪除器。

復(fù)制代碼
#include <iostream>
#include <stdio.h> #include <memory> using namespace std; void closePf(FILE * pf) { cout<<"----close pf after works!----"<<endl; fclose(pf); } int main() { // FILE * fp2 = fopen("bin2.txt", "w"); // if(!pf) // return -1; // char *buf = "abcdefg"; // fwrite(buf, 8, 1, fp2); // fclose(fp2); shared_ptr<FILE> pf(fopen("bin2.txt", "w"),closePf); cout<<"*****start working****"<<endl; if(!pf) return -1; char *buf = "abcdefg"; fwrite(buf, 8, 1, pf.get()); //!確保fwrite不會(huì)刪除指針的情況下,可以將shared_ptr內(nèi)置指針取出來(lái)。 cout<<"----write int file!-----"<<endl; }                 //!即可以避免異常發(fā)生后無(wú)法釋放內(nèi)存的問(wèn)題,也避免了很多人忘記執(zhí)行fclose的問(wèn)題。
復(fù)制代碼

  在這里可以設(shè)想一下TCP/IP中鏈接的打開(kāi)和關(guān)閉的情況,同理都可以使用智能指針來(lái)管理。

 

 總結(jié):

  最后總結(jié)一下上面所陳述的內(nèi)容,也是shared_ptr使用的基本規(guī)范

1)不使用相同的內(nèi)置指針值初始化(或reset)多個(gè)智能指針。

2)不delete get函數(shù)返回的指針。

3)如果你使用了get返回的指針,記住當(dāng)最后一個(gè)對(duì)應(yīng)的智能指針?shù)N毀后,你的指針就變?yōu)闊o(wú)效了。

4)如果你使用智能指針管理的資源不是new分配的內(nèi)存,記得傳遞給他一個(gè)刪除器。

 

 weak_ptr

 weakptr使用的比較少,如有興趣了解,請(qǐng)去參考該篇文章:https://www.cnblogs.com/DswCnblog/p/5628314.html

 

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

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    好吊一区二区三区在线看| 欧美日韩国产福利在线观看| 欧美日韩国产一级91| 亚洲一区二区久久观看| 国产精品内射视频免费| 手机在线不卡国产视频| 久久热九九这里只有精品| 亚洲一区二区亚洲日本| 青青草草免费在线视频| 中文字幕乱码一区二区三区四区| 日韩欧美在线看一卡一卡| 99久久精品视频一区二区| 日本人妻丰满熟妇久久| 中文字幕乱码亚洲三区| av国产熟妇露脸在线观看| 丁香七月啪啪激情综合| 中文字幕日韩精品人一妻| 国产欧美日韩综合精品二区| 亚洲国产综合久久天堂| 日韩一区二区三区观看| 欧美大黄片在线免费观看| 男生和女生哪个更好色| 日韩精品少妇人妻一区二区| 成人精品日韩专区在线观看| 国产成人免费激情视频| 国产精品十八禁亚洲黄污免费观看 | 久久久精品区二区三区| 91人妻人人精品人人爽| 中文字幕人妻综合一区二区| 精品少妇人妻av免费看| 国产精品欧美激情在线观看| 91欧美日韩一区人妻少妇| 91精品国产综合久久福利| 国产精品白丝一区二区| 亚洲熟女诱惑一区二区| 老司机精品福利视频在线播放| 国产伦精品一区二区三区高清版| 九九九热视频最新在线| 五月婷婷六月丁香在线观看| 老司机精品线观看86| 精品女同一区二区三区|