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

分享

C 11中智能指針的原理、使用、實(shí)現(xiàn)

 印度阿三17 2021-01-22

 目錄

    • 理解智能指針的原理

    • 智能指針的使用

    • 智能指針的設(shè)計(jì)和實(shí)現(xiàn)

1.智能指針的作用

       C 程序設(shè)計(jì)中使用堆內(nèi)存是非常頻繁的操作,堆內(nèi)存的申請和釋放都由程序員自己管理。程序員自己管理堆內(nèi)存可以提高了程序的效率,但是整體來說堆內(nèi)存的管理是麻煩的,C 11中引入了智能指針的概念,方便管理堆內(nèi)存。使用普通指針,容易造成堆內(nèi)存泄露(忘記釋放),二次釋放,程序發(fā)生異常時(shí)內(nèi)存泄露等問題等,使用智能指針能更好的管理堆內(nèi)存。

理解智能指針需要從下面三個(gè)層次:

  1. 從較淺的層面看,智能指針是利用了一種叫做RAII(資源獲取即初始化)的技術(shù)對普通的指針進(jìn)行封裝,這使得智能指針實(shí)質(zhì)是一個(gè)對象,行為表現(xiàn)的卻像一個(gè)指針。

  2. 智能指針的作用是防止忘記調(diào)用delete釋放內(nèi)存和程序異常的進(jìn)入catch塊忘記釋放內(nèi)存。另外指針的釋放時(shí)機(jī)也是非常有考究的,多次釋放同一個(gè)指針會造成程序崩潰,這些都可以通過智能指針來解決。

  3. 智能指針還有一個(gè)作用是把值語義轉(zhuǎn)換成引用語義。C 和Java有一處最大的區(qū)別在于語義不同,在Java里面下列代碼:

  Animal a = new Animal();

  Animal b = a;

     你當(dāng)然知道,這里其實(shí)只生成了一個(gè)對象,a和b僅僅是把持對象的引用而已。但在C 中不是這樣,

     Animal a;

     Animal b = a;

     這里卻是就是生成了兩個(gè)對象。

     關(guān)于值語言參考這篇文章http://www.cnblogs.com/Solstice/archive/2011/08/16/2141515.html

2.智能指針的使用

智能指針在C 11版本之后提供,包含在頭文件<memory>中,shared_ptr、unique_ptr、weak_ptr

2.1 shared_ptr的使用

shared_ptr多個(gè)指針指向相同的對象。shared_ptr使用引用計(jì)數(shù),每一個(gè)shared_ptr的拷貝都指向相同的內(nèi)存。每使用他一次,內(nèi)部的引用計(jì)數(shù)加1,每析構(gòu)一次,內(nèi)部的引用計(jì)數(shù)減1,減為0時(shí),自動(dòng)刪除所指向的堆內(nèi)存。shared_ptr內(nèi)部的引用計(jì)數(shù)是線程安全的,但是對象的讀取需要加鎖。

  • 初始化。智能指針是個(gè)模板類,可以指定類型,傳入指針通過構(gòu)造函數(shù)初始化。也可以使用make_shared函數(shù)初始化。不能將指針直接賦值給一個(gè)智能指針,一個(gè)是類,一個(gè)是指針。例如std::shared_ptr<int> p4 = new int(1);的寫法是錯(cuò)誤的

  • 拷貝和賦值??截愂沟脤ο蟮囊糜?jì)數(shù)增加1,賦值使得原對象引用計(jì)數(shù)減1,當(dāng)計(jì)數(shù)為0時(shí),自動(dòng)釋放內(nèi)存。后來指向的對象引用計(jì)數(shù)加1,指向后來的對象。

  • get函數(shù)獲取原始指針

  • 注意不要用一個(gè)原始指針初始化多個(gè)shared_ptr,否則會造成二次釋放同一內(nèi)存

  • 注意避免循環(huán)引用,shared_ptr的一個(gè)最大的陷阱是循環(huán)引用,循環(huán),循環(huán)引用會導(dǎo)致堆內(nèi)存無法正確釋放,導(dǎo)致內(nèi)存泄漏。循環(huán)引用在weak_ptr中介紹。

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

int main() {
    {
        int a = 10;
        std::shared_ptr<int> ptra = std::make_shared<int>(a);
        std::shared_ptr<int> ptra2(ptra); //copy
        std::cout << ptra.use_count() << std::endl;

        int b = 20;
        int *pb = &a;
        //std::shared_ptr<int> ptrb = pb;  //error
        std::shared_ptr<int> ptrb = std::make_shared<int>(b);
        ptra2 = ptrb; //assign
        pb = ptrb.get(); //獲取原始指針

        std::cout << ptra.use_count() << std::endl;
        std::cout << ptrb.use_count() << std::endl;
    }
}
復(fù)制代碼

2.2 unique_ptr的使用

  unique_ptr“唯一”擁有其所指對象,同一時(shí)刻只能有一個(gè)unique_ptr指向給定對象(通過禁止拷貝語義、只有移動(dòng)語義來實(shí)現(xiàn))。相比與原始指針unique_ptr用于其RAII的特性,使得在出現(xiàn)異常的情況下,動(dòng)態(tài)資源能得到釋放。unique_ptr指針本身的生命周期:從unique_ptr指針創(chuàng)建時(shí)開始,直到離開作用域。離開作用域時(shí),若其指向?qū)ο?,則將其所指對象銷毀(默認(rèn)使用delete操作符,用戶可指定其他操作)。unique_ptr指針與其所指對象的關(guān)系:在智能指針生命周期內(nèi),可以改變智能指針?biāo)笇ο?,如?chuàng)建智能指針時(shí)通過構(gòu)造函數(shù)指定、通過reset方法重新指定、通過release方法釋放所有權(quán)、通過移動(dòng)語義轉(zhuǎn)移所有權(quán)。

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

int main() {
    {
        std::unique_ptr<int> uptr(new int(10));  //綁定動(dòng)態(tài)對象
        //std::unique_ptr<int> uptr2 = uptr;  //不能賦值
        //std::unique_ptr<int> uptr2(uptr);  //不能拷貝
        std::unique_ptr<int> uptr2 = std::move(uptr); //轉(zhuǎn)換所有權(quán)
        uptr2.release(); //釋放所有權(quán)
    }
    //超過uptr的作用域,內(nèi)存釋放
}
復(fù)制代碼

2.3 weak_ptr的使用

  weak_ptr是為了配合shared_ptr而引入的一種智能指針,因?yàn)樗痪哂衅胀ㄖ羔樀男袨椋瑳]有重載operator*和->,它的最大作用在于協(xié)助shared_ptr工作,像旁觀者那樣觀測資源的使用情況。weak_ptr可以從一個(gè)shared_ptr或者另一個(gè)weak_ptr對象構(gòu)造,獲得資源的觀測權(quán)。但weak_ptr沒有共享資源,它的構(gòu)造不會引起指針引用計(jì)數(shù)的增加。使用weak_ptr的成員函數(shù)use_count()可以觀測資源的引用計(jì)數(shù),另一個(gè)成員函數(shù)expired()的功能等價(jià)于use_count()==0,但更快,表示被觀測的資源(也就是shared_ptr的管理的資源)已經(jīng)不復(fù)存在。weak_ptr可以使用一個(gè)非常重要的成員函數(shù)lock()從被觀測的shared_ptr獲得一個(gè)可用的shared_ptr對象, 從而操作資源。但當(dāng)expired()==true的時(shí)候,lock()函數(shù)將返回一個(gè)存儲空指針的shared_ptr。

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

int main() {
    {
        std::shared_ptr<int> sh_ptr = std::make_shared<int>(10);
        std::cout << sh_ptr.use_count() << std::endl;

        std::weak_ptr<int> wp(sh_ptr);
        std::cout << wp.use_count() << std::endl;

        if(!wp.expired()){
            std::shared_ptr<int> sh_ptr2 = wp.lock(); //get another shared_ptr
            *sh_ptr = 100;
            std::cout << wp.use_count() << std::endl;
        }
    }
    //delete memory
}
復(fù)制代碼

2.4 循環(huán)引用

考慮一個(gè)簡單的對象建?!议L與子女:a Parent has a Child, a Child knowshis/her Parent。在Java 里邊很好寫,不用擔(dān)心內(nèi)存泄漏,也不用擔(dān)心空懸指針,只要正確初始化myChild 和myParent,那么Java 程序員就不用擔(dān)心出現(xiàn)訪問錯(cuò)誤。一個(gè)handle 是否有效,只需要判斷其是否non null。

public class Parent
{
  private Child myChild;
}
public class Child
{
  private Parent myParent;
}
在C 里邊就要為資源管理費(fèi)一番腦筋。如果使用原始指針作為成員,Child和Parent由誰釋放?那么如何保證指針的有效性?如何防止出現(xiàn)空懸指針?這些問題是C 面向?qū)ο缶幊搪闊┑膯栴},現(xiàn)在可以借助smart pointer把對象語義(pointer)轉(zhuǎn)變?yōu)橹担╲alue)語義,shared_ptr輕松解決生命周期的問題,不必?fù)?dān)心空懸指針。但是這個(gè)模型存在循環(huán)引用的問題,注意其中一個(gè)指針應(yīng)該為weak_ptr。

原始指針的做法,容易出錯(cuò)

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

class Child;
class Parent;

class Parent {
private:
    Child* myChild;
public:
    void setChild(Child* ch) {
        this->myChild = ch;
    }

    void doSomething() {
        if (this->myChild) {

        }
    }

    ~Parent() {
        delete myChild;
    }
};

class Child {
private:
    Parent* myParent;
public:
    void setPartent(Parent* p) {
        this->myParent = p;
    }
    void doSomething() {
        if (this->myParent) {

        }
    }
    ~Child() {
        delete myParent;
    }
};

int main() {
    {
        Parent* p = new Parent;
        Child* c =  new Child;
        p->setChild(c);
        c->setPartent(p);
        delete c;  //only delete one
    }
    return 0;
}
復(fù)制代碼

循環(huán)引用內(nèi)存泄露的問題

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

class Child;
class Parent;

class Parent {
private:
    std::shared_ptr<Child> ChildPtr;
public:
    void setChild(std::shared_ptr<Child> child) {
        this->ChildPtr = child;
    }

    void doSomething() {
        if (this->ChildPtr.use_count()) {

        }
    }

    ~Parent() {
    }
};

class Child {
private:
    std::shared_ptr<Parent> ParentPtr;
public:
    void setPartent(std::shared_ptr<Parent> parent) {
        this->ParentPtr = parent;
    }
    void doSomething() {
        if (this->ParentPtr.use_count()) {

        }
    }
    ~Child() {
    }
};

int main() {
    std::weak_ptr<Parent> wpp;
    std::weak_ptr<Child> wpc;
    {
        std::shared_ptr<Parent> p(new Parent);
        std::shared_ptr<Child> c(new Child);
        p->setChild(c);
        c->setPartent(p);
        wpp = p;
        wpc = c;
        std::cout << p.use_count() << std::endl; // 2
        std::cout << c.use_count() << std::endl; // 2
    }
    std::cout << wpp.use_count() << std::endl;  // 1
    std::cout << wpc.use_count() << std::endl;  // 1
    return 0;
}
復(fù)制代碼

正確的做法

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

class Child;
class Parent;

class Parent {
private:
    //std::shared_ptr<Child> ChildPtr;
    std::weak_ptr<Child> ChildPtr;
public:
    void setChild(std::shared_ptr<Child> child) {
        this->ChildPtr = child;
    }

    void doSomething() {
        //new shared_ptr
        if (this->ChildPtr.lock()) {

        }
    }

    ~Parent() {
    }
};

class Child {
private:
    std::shared_ptr<Parent> ParentPtr;
public:
    void setPartent(std::shared_ptr<Parent> parent) {
        this->ParentPtr = parent;
    }
    void doSomething() {
        if (this->ParentPtr.use_count()) {

        }
    }
    ~Child() {
    }
};

int main() {
    std::weak_ptr<Parent> wpp;
    std::weak_ptr<Child> wpc;
    {
        std::shared_ptr<Parent> p(new Parent);
        std::shared_ptr<Child> c(new Child);
        p->setChild(c);
        c->setPartent(p);
        wpp = p;
        wpc = c;
        std::cout << p.use_count() << std::endl; // 2
        std::cout << c.use_count() << std::endl; // 1
    }
    std::cout << wpp.use_count() << std::endl;  // 0
    std::cout << wpc.use_count() << std::endl;  // 0
    return 0;
}
復(fù)制代碼

3.智能指針的設(shè)計(jì)和實(shí)現(xiàn)

  下面是一個(gè)簡單智能指針的demo。智能指針類將一個(gè)計(jì)數(shù)器與類指向的對象相關(guān)聯(lián),引用計(jì)數(shù)跟蹤該類有多少個(gè)對象共享同一指針。每次創(chuàng)建類的新對象時(shí),初始化指針并將引用計(jì)數(shù)置為1;當(dāng)對象作為另一對象的副本而創(chuàng)建時(shí),拷貝構(gòu)造函數(shù)拷貝指針并增加與之相應(yīng)的引用計(jì)數(shù);對一個(gè)對象進(jìn)行賦值時(shí),賦值操作符減少左操作數(shù)所指對象的引用計(jì)數(shù)(如果引用計(jì)數(shù)為減至0,則刪除對象),并增加右操作數(shù)所指對象的引用計(jì)數(shù);調(diào)用析構(gòu)函數(shù)時(shí),構(gòu)造函數(shù)減少引用計(jì)數(shù)(如果引用計(jì)數(shù)減至0,則刪除基礎(chǔ)對象)。智能指針就是模擬指針動(dòng)作的類。所有的智能指針都會重載 -> 和 * 操作符。智能指針還有許多其他功能,比較有用的是自動(dòng)銷毀。這主要是利用棧對象的有限作用域以及臨時(shí)對象(有限作用域?qū)崿F(xiàn))析構(gòu)函數(shù)釋放內(nèi)存。

復(fù)制代碼
 1 #include <iostream>
 2 #include <memory>
 3 
 4 template<typename T>
 5 class SmartPointer {
 6 private:
 7     T* _ptr;
 8     size_t* _count;
 9 public:
10     SmartPointer(T* ptr = nullptr) :
11             _ptr(ptr) {
12         if (_ptr) {
13             _count = new size_t(1);
14         } else {
15             _count = new size_t(0);
16         }
17     }
18 
19     SmartPointer(const SmartPointer& ptr) {
20         if (this != &ptr) {
21             this->_ptr = ptr._ptr;
22             this->_count = ptr._count;
23             (*this->_count)  ;
24         }
25     }
26 
27     SmartPointer& operator=(const SmartPointer& ptr) {
28         if (this->_ptr == ptr._ptr) {
29             return *this;
30         }
31 
32         if (this->_ptr) {
33             (*this->_count)--;
34             if (this->_count == 0) {
35                 delete this->_ptr;
36                 delete this->_count;
37             }
38         }
39 
40         this->_ptr = ptr._ptr;
41         this->_count = ptr._count;
42         (*this->_count)  ;
43         return *this;
44     }
45 
46     T& operator*() {
47         assert(this->_ptr == nullptr);
48         return *(this->_ptr);
49 
50     }
51 
52     T* operator->() {
53         assert(this->_ptr == nullptr);
54         return this->_ptr;
55     }
56 
57     ~SmartPointer() {
58         (*this->_count)--;
59         if (*this->_count == 0) {
60             delete this->_ptr;
61             delete this->_count;
62         }
63     }
64 
65     size_t use_count(){
66         return *this->_count;
67     }
68 };
69 
70 int main() {
71     {
72         SmartPointer<int> sp(new int(10));
73         SmartPointer<int> sp2(sp);
74         SmartPointer<int> sp3(new int(20));
75         sp2 = sp3;
76         std::cout << sp.use_count() << std::endl;
77         std::cout << sp3.use_count() << std::endl;
78     }
79     //delete operator
80 }
復(fù)制代碼

參考:

  1. 值語義:http://www.cnblogs.com/Solstice/archive/2011/08/16/2141515.html

  2. shared_ptr使用:http://www.cnblogs.com/jiayayao/archive/2016/12/03/6128877.html

  3. unique_ptr使用:http://blog.csdn.net/pi9nc/article/details/12227887

  4. weak_ptr的使用:http://blog.csdn.net/mmzsyx/article/details/8090849

  5. weak_ptr解決循環(huán)引用的問題:http://blog.csdn.net/shanno/article/details/7363480

  6. C 面試題(四)——智能指針的原理和實(shí)現(xiàn)

來源:https://www./content-1-830101.html

    本站是提供個(gè)人知識管理的網(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)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    一区二区三区日韩经典| 人妻一区二区三区多毛女| 日韩蜜桃一区二区三区| 国产一区二区熟女精品免费| 日本本亚洲三级在线播放| 国产日韩综合一区在线观看| 97人妻精品一区二区三区免| 国产精品夜色一区二区三区不卡 | 精品伊人久久大香线蕉综合| 亚洲欧洲在线一区二区三区| 国产精品成人免费精品自在线观看| 天海翼精品久久中文字幕| 亚洲欧美日韩在线看片| 欧美日韩亚洲巨色人妻| 国产传媒精品视频一区| 欧美高潮喷吹一区二区| 国产一级精品色特级色国产| 国产亚洲不卡一区二区| 九九九热在线免费视频| 日韩欧美中文字幕人妻| 深夜视频在线观看免费你懂| 亚洲中文字幕综合网在线 | 国产水滴盗摄一区二区| 国产成人精品综合久久久看| 国产一区麻豆水好多高潮| 国产中文字幕一二三区| 欧美成人黄色一级视频| 国产日韩欧美在线播放| 97人妻精品一区二区三区男同| 出差被公高潮久久中文字幕| 老司机激情五月天在线不卡| 国产一区欧美一区二区| 色综合视频一区二区观看| 国产成人亚洲精品青草天美| a久久天堂国产毛片精品| 久久国产精品亚州精品毛片| 欧美精品二区中文乱码字幕高清| 麻豆亚州无矿码专区视频| 免费在线观看激情小视频| 国产不卡的视频在线观看| 中文字幕佐山爱一区二区免费|