細說智能指針

提到指針,咱們就會想到指針的高效,固然,濫用指針也會爲咱們帶來許多的潛在bug。
提到指針,咱們就會想到內存泄漏。好比,使用指針後忘記釋放,長此以往,堆空間就會所有使用完,那麼會帶來很大的危害。再好比,兩個指針指向同一片內存區域,咱們對同一片區域進行了屢次釋放,一樣會形成內存泄漏。
爲了方便你們的理解,咱們先來模擬一下,使用指針卻忘記釋放帶來的危害。首先,咱們要定義一個類。此次,仍是定義女友類吧(以前寫過一篇《細說C++的友元》用的就是女友類,此次還用這個吧,方便說明問題,更況且咱們這羣碼畜怎麼會有女友呢,沒有女友只能本身建立了,哈哈哈哈)。
女生都喜歡自拍,尤爲是漂亮的女生。因此,女生會有不少照片,對吧。那麼,咱們建立的這個女友類,就讓她有照片吧。固然了,你女友的照片確定不會隨便給別人的吧,因此要把picutre這個變量聲明爲private類型。既然,女生喜歡自拍,而且發朋友圈,也就是說,其餘人雖然得不到她的照片,卻能夠經過她的朋友圈看到她的自拍,也就意味着咱們能夠經過一個public函數訪問picture這個變量。那麼,咱們如今來寫代碼。ios

class Girlfriend{
private:
    int pictures;
public:
    Girlfriend ( int i ){
        cout << "Girlfriend ( int i ) " << endl;   /*這句代碼是不須要的,
        寫在這裏只是爲了後期可以方便咱們觀察
        */
        this->pictures = i;
    }
    int getPic ( void ){
        return this->pictures;
    }
    ~Girlfriend (){
        cout << "~Girlfriend() " << endl;  /*這句代碼是不須要的,
        寫在這裏只是方便咱們觀察
        */
    }
};

接着,咱們來寫一個主函數,來使用這個女友類。ide

int main ( int argc, char** argv ){
    Girlfriend* Alice = new Girlfriend( 100 );
    cout << "my girlfriend's pictures are " << mp->getPic() << endl;
    system ( "pause" );
    return 0;
}

運行結果:
細說智能指針
咱們在主函數中作了什麼事情呢?咱們經過指針動態建立了一個對象,而且,我建立的這個女友Alice有100張照片。看到這裏,不少人就會想,指針危險嗎?好像沒什麼危險啊。不是使用正常嗎,看到程序運行結果,程序不是完美的執行了嘛,好像也沒什麼。
首先,咱們建立的這個指針Alice沒有去釋放,僅僅建立了一個,危害不大,可是多了以後呢。那好比,咱們如今再來寫一個主函數。函數

int main ( int argc, char** argv ){
    Girlfriend* Alice = new Girlfriend( 100 );
    Girlfriend* Lisa = new Girlfriend( 200 );

    cout << "my girlfriend Alice's pictures are " << Alice->getPic() << endl;
    cout << "my girlfriend Lisa's pictures are " << Lisa->getPic() << endl;
    system ( "pause" );
    return 0;
}

運行結果:
細說智能指針
咱們先拋開指針不談。咱們就來談談女友。假如Alice是你女友,你手機裏有她的100張照片,後來大家由於一些事情產生了矛盾分手了,因而你交了另一個女友Lisa。有一次,Lisa翻你的手機,發現你居然有100張前女朋友照片,她什麼感覺,估計要瘋了,你說後果嚴不嚴重,因此,趁你的現女朋友沒發現以前,趕忙把前女朋友照片給刪了,趕忙把前女朋友照片釋放掉,否則,後果……
如今,迴歸正題,咱們使用指針建立對象,一個,兩個,都沒問題,那若是多了呢?100個,1000個,10000個,那內存還能受的了嗎?因此,用完指針以後必定要釋放指針。
可是,咱們是人啊,不是機器,總會有遺忘的時候,那麼咱們有沒有辦法在指針使用完畢後,它本身釋放呢?當有了這個需求後,咱們就要開始思考解決辦法了。很快,咱們就有了解決方案:經過類對象模擬指針。指針有兩個運算符,一個是->,另外一個是星號,只要在類內重載這兩個操做符就好了。那麼,這個類的成員變量是什麼呢?就是一個指針。經過類來模擬指針的,這就是智能指針了。如今,咱們先來寫一個簡陋版的。測試

class SmartPointer{
private:
    Girlfriend* sp;
public:
    SmartPointer ( Girlfriend* p = NULL ){
        sp = p;
    }
    Girlfriend* operator -> (){
        return sp;
    }
    Girlfriend& operator * (){
        return *sp;
    }
    ~SmartPointer (){
        delete sp;
        cout << "~SmartPointer() " << endl;  /*這句代碼徹底不必,
        寫在這裏只是便於咱們觀察    
        */
    }
}

咱們在這個智能指針類中,重載指針了指針的兩個運算符。經過這樣作,咱們解決了使用完畢指針後自動釋放的問題。那麼,還有一個問題,若是兩個指針指向同一片區域,這樣在釋放指針時也會形成內存泄漏,由於同一片區域被釋放兩次。這個問題怎麼解決呢?經過重載拷貝構造函數和賦值操做符。咱們如今類內實現拷貝構造函數。this

SmartPointer ( const SmartPointer& obj ){
    sp = obj.sp;
    const_cast<SmartPointer&>(obj).sp = NULL;
}

咱們實現了拷貝構造函數,由於同一片區域只能有一個指針指向,因此,咱們在把obj指向的地址賦給sp後,要將obj的sp賦值爲NULL。這裏用到了一個強制類型轉換。
如今,咱們要在類內實現重載賦值操做符。spa

SmartPointer& operator = ( const SmartPointer& obj ){
    if ( this != &obj ){
        delete sp;
        sp = obj.sp;
        obj.sp = NULL;
    }
    return *this;
}

到這裏爲止,咱們就把智能指針類實現完畢了。同時,使用智能指針還能夠避免指針比較或加減運算,由於這些運算會形成指針越界帶來的bug。好了,咱們看一下,運行結果,符不符合咱們的預期。
細說智能指針
哇,很是符合。指針使用完畢後完美釋放。
下面是完整代碼:指針

#include <iostream>
#include <string>
using namespace std;
class Girlfriend{
private:
    int pictures;
public:
    Girlfriend ( int i ){
        this->pictures = i;
        cout << "Girlfriend ( int i )" << endl;
    }
    int getPic ( void ){
        return this->pictures;
    }
    ~Girlfriend (){
        cout << "~Girlfriend ()" << endl;
    }
};

class SmartPointer{
private:
    Girlfriend* sp;
public:
    SmartPointer ( Girlfriend* p = NULL ){
        sp = p;
        cout << "SmartPointer ( Girlfriend* p = NULL )" << endl;
    }
    Girlfriend* operator -> (){
        return sp;
    }
    Girlfriend& operator * (){
        return *sp;
    }
    SmartPointer ( const SmartPointer& obj ){
        sp = obj.sp;
        const_cast<SmartPointer&>(obj).sp = NULL;
    }
    SmartPointer& operator = ( const SmartPointer& obj ){
        if ( this != &obj ){
            delete sp;
            sp = obj.sp;
            const_cast<SmartPointer&>(obj).sp = NULL;
        }
        return *this;
    }
    bool isnull (){
        return ( sp == NULL );
    }
    ~SmartPointer (){
        cout << "~SmartPointer()" << endl;
        delete sp;
    }
};

int main ( int argc, char** argv ){
    SmartPointer Alice = new Girlfriend( 100 );
    SmartPointer Lisa = new Girlfriend( 200 );

    cout << "my girlfriend Alice's pictures are " << Alice->getPic() << endl;
    cout << "my girlfriend Lisa's pictures are " << Lisa->getPic() << endl;

    system ( "pause" );
    return 0;
}

後記:
這篇文章寫完也是花了我兩個小時了,這其中的代碼都是通過我本身電腦測試的。雖然寫這篇文章只花了我兩個小時,可是,我在寫這篇文章以前就已經開始在構思了,該如何寫,才能作到通俗易懂。看來真的,想寫好一篇文章仍是比較困難的,畢竟要把本身學會的東西,經過語言文字描述出來,仍是不容易的。以前寫的一篇《細說C++的友元》上了博客的推薦,這裏很是感謝51cto的小編。由於,那篇文章的緣故,我全力以赴的寫了這篇文章。
但願看完的小夥伴可以學到些知識,若是以爲我哪裏講解的有誤,也能夠在評論中指出,或者哪裏有不懂的地方,也能夠在評論中留言,若是有空,咱們能夠探討下。
同時,感謝51cto的小夥伴們,大佬們花時間耐着性子看完了這篇文章,謝謝,謝謝大家!
最後,固然是但願,看完的小夥伴們,有所收穫後點個贊,表示支持。謝謝!code

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------歡迎打賞!哈哈哈哈!
細說智能指針對象

相關文章
相關標籤/搜索