當(dāng)前位置:首頁(yè) > 公眾號(hào)精選 > 21ic電子網(wǎng)
[導(dǎo)讀]來(lái)自:wxquare 鏈接:https://www.cnblogs.com/wxquare/p/4759020.html 目錄 理解智能指針的原理 智能指針的使用 智能指針的設(shè)計(jì)和實(shí)現(xiàn) 1、智能指針的作用 C++程序設(shè)計(jì)中使用堆內(nèi)存是非常頻繁的操作,堆內(nèi)存的申請(qǐng)和釋放都由程序員自己管理。程序員自己管理堆



來(lái)自:wxquare

鏈接:https://www.cnblogs.com/wxquare/p/4759020.html

目錄

  • 理解智能指針的原理

  • 智能指針的使用

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

1、智能指針的作用

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

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

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

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

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

Animal a = new Animal();
Animal b = a;

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

Animal a;
Animal b = a;

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

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

2、智能指針的使用

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

2.1 shared_ptr的使用

shared_ptr多個(gè)指針指向相同的對(duì)象。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ù)是線(xiàn)程安全的,但是對(duì)象的讀取需要加鎖。

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

  • 拷貝和賦值。 拷貝使得對(duì)象的引用計(jì)數(shù)增加1,賦值使得原對(duì)象引用計(jì)數(shù)減1,當(dāng)計(jì)數(shù)為0時(shí),自動(dòng)釋放內(nèi)存。后來(lái)指向的對(duì)象引用計(jì)數(shù)加1,指向后來(lái)的對(duì)象

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

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

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

#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;
    }
}

2.2 unique_ptr的使用

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

#include <iostream>
#include <memory>

int main() {
    {
        std::unique_ptr<int> uptr(new int(10));  //綁定動(dòng)態(tài)對(duì)象
        //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)
    }
    //超過(guò)uptr的作用域,內(nèi)存釋放
}

2.3 weak_ptr的使用

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

#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
}

2.4 循環(huán)引用

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

public class Parent
{
  private Child myChild;
}
public class Child
{
  private Parent myParent;
}

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

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

#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;
}

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

#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;
}

正確的做法

#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;
}

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

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

 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 }

參考:

1、值語(yǔ)義: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)引用的問(wèn)題:http://blog.csdn.net/shanno/article/details/7363480
6、C++面試題(四)——智能指針的原理和實(shí)現(xiàn)https://blog.csdn.net/worldwindjp/article/details/18843087#comments



免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!

21ic電子網(wǎng)

掃描二維碼,關(guān)注更多精彩內(nèi)容

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專(zhuān)欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車(chē)的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車(chē)技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車(chē)工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車(chē)。 SODA V工具的開(kāi)發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車(chē) 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來(lái)越多用戶(hù)希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來(lái)越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開(kāi)幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱(chēng),數(shù)字世界的話(huà)語(yǔ)權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱(chēng)"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉