1. 程式人生 > >智慧指標之 Auto_Ptr

智慧指標之 Auto_Ptr

在介紹智慧指標之前我們先看以下程式碼:

void FunTest()
{
    int *p = new int[10];
    FILE* pFile = fopen( "1.txt", "w" );
    if (pFile == NULL)
    {
        return;
    }
    // DoSomethint();
    if (p != NULL)
    {
        delete[] p;
        p = NULL;
    }
}

在該程式碼中,我們注意了對指標的釋放卻忽略了對檔案指標的關閉。C++中的動態記憶體需要使用者自己來維護,動態開闢的空間,在出函式作用域或者程式正常退出前必須釋放掉,否則會造成記憶體洩露,有時我們已經非常謹慎了,然防不勝防。

智慧指標(RAII)的存在一開始就是為了解決資源分配即初始化,其解決方案是定義一個類來封裝資源的分配和釋放,在建構函式完成資源的分配和初始化,在解構函式完成資源的清理,可以保證資源的正確初始化和釋放。

所以有了第一代Auto_Ptr

template<class T>
class AutoPtr
{
public:
    AutoPtr(T* ptr = NULL)
        :_ptr(ptr)
    {}

    AutoPtr(AutoPtr<T>& s)
        :_ptr(s._ptr)
    {
        s._ptr = NULL;
        //經過賦值拷貝,原本的智慧指標s中_ptr 已經指向空
} AutoPtr<T>& operator=(AutoPtr<T>& s) { if (this != &s) { if (_ptr) { delete _ptr; } _ptr = s._ptr; s._ptr = NULL; //經過賦值運算子後同樣被置空 } return
*this; } T* operator->()const { return _ptr; } T& operator*()const { return *_ptr; } ~AutoPtr() { if (_ptr) { delete _ptr; } } private: T* _ptr; }; void Funtest1(AutoPtr<char> p) {} void Funtest2(AutoPtr<char>& p) {} int main() { AutoPtr<char> p1(new char); AutoPtr<char> p2(p1); //AutoPtr<char> p4(p3); 出錯,不能通過編譯,原因在拷貝建構函式中更改了具有常性的 p3 //Funtest1(p2); 同理,以傳值形式作為函式形參,也要經過拷貝建構函式形成臨時變數p,同理賦值崩潰 Funtest2(p2); //以引用的方式傳遞沒有問題 *p2 = '1'; return 0; }

由此可見一代Auto_Ptr的缺點都有以下幾點:
1:在Auto_Ptr中經過資源轉移後,p1不能再訪問空間
2:以值傳遞的形式作為函式引數後,一旦呼叫函式,該智慧指標在原函式中也不能訪問空間
3:const物件不能構造同類型物件

針對第一代Auto_Ptr的問題,於是乎有了第二代Auto_Ptr的產生實際上我們是使用一個bool 型別的 資料成員來表明該智慧指標是否具有被析構的能力

template<class T>
class AutoPtr
{
public:
    AutoPtr(T* ptr = NULL)
           :_ptr(ptr)
           , type(false)
    {
        if (_ptr)
            type = true;
    }

    AutoPtr(const AutoPtr<T>& s)
        :_ptr(s._ptr)
        , type(s.type)
    {
        s.type = false;
    }

//拷貝建構函式中,將s物件具有析構的能力 type賦給新物件,而s.type賦值為false,說明不具有析構能力
    AutoPtr<T>& operator=(const AutoPtr<T>& s)
    {
        if (this != &s)
        {
            if (_ptr && type)
            {
                delete _ptr;
            }
            _ptr = s._ptr;
            type = s.type;
            s.type = false;
        }
        return *this;
    }

    T& operator*()const
    {
        return *_ptr;
    }
 //解構函式中,我們將 type 為true的智慧指標進行析構,從而避開了對同一空間釋放多次的問題
    ~AutoPtr()
    {
        if (_ptr && type)
        {
            delete _ptr;
            _ptr = NULL;
            type = false;
        }
    }

private:
    T* _ptr;
    mutable bool type;
    //在成員函式體內只改變了type的值,針對於const物件,我們加關鍵字mutable可以更改type的值
};

void Funtest1(AutoPtr<char> p)
{}

int main()
{
    AutoPtr<char> p1(new char);
    void Funtest1(p1);
    AutoPtr<char> p2(p1);
    AutoPtr<char> p3(new char);
    AutoPtr<char> p4(p3);
    Funtest1(p1);
    p1 = p3;
    *p3 ='1';
    return 0;
}

經過驗證,我們確定基於第一代的修改我們成功的引入了第二代,但是我們引入了一個野指標的問題,一旦對於野指標進行操作,很容易造成問題,

當我們用智慧指標管理一個無名的智慧指標物件時,編譯器會自動優化,呼叫系統的建構函式,如果我們想呼叫我們自己寫的建構函式,我們推匯出智慧指標的第三個版本

template<class T>
class autoptrref
{
public:
    autoptrref(T* ptr = NULL)
        :_ptr(ptr)
    {}
    T* _ptr;
};

template<class T>
class autoptr
{
public:
    //建構函式
    autoptr(T* ptr = NULL)
        :_ptr(ptr)
    {}

    //拷貝建構函式
    autoptr(autoptr<T>& ap)
        :_ptr(ap._ptr)
    {
        ap._ptr = NULL;//拷貝完後將ap._ptr置NULL
    }

    //賦值運算子的過載
    autoptr<int>& operator=(autoptr<T>& ap)
    {
        if (this != &ap)
        {
            if (_ptr != NULL)
            {
                delete _ptr;
            }
            _ptr = ap._ptr;
            ap._ptr = NULL;//賦值完後將ap._ptr置NULL
        }
        return *this;
    }

    //解決辦法

    //過載autoptrref()
    operator autoptrref<T>()//將autoptr<T>轉化為autoptrref<T>
    {
        autoptrref<T> temp(_ptr);
        _ptr = NULL;
        return temp;
    }

    //再過載一個拷貝建構函式
    autoptr(const autoptrref<T>& apr)
        :_ptr(apr._ptr)
    {}

    ~autoptr()//解構函式
    {
        if (_ptr != NULL)
        {
            delete _ptr;
        }
    }


    //*的過載
    T& operator*()const
    {
        return *_ptr;
    }

    //得到原生態指標
    T* Get()const
    {
        return _ptr;
    }

private:
    T* _ptr;
};
void funtest()
{
    autoptr<int> ap(autoptrref<int>(autoptr<int>(new int)));
    //用無名的智慧指標拷貝構造智慧指標ap,則不會呼叫我們編寫好得拷貝建構函式,而是呼叫系統自動合成個的拷貝建構函式
}    //原因是編譯器的自動優化,解決辦法:重新增加一個類autoptrref,將無名物件的型別轉化為autoptrref                                       

int main()
{
    funtest();
    return 0;
}

相關推薦

C++智慧指標auto_ptr

標頭檔案<memory> 總結為一句話:auto_ptr是獨佔指標,它的出現是能夠自動析構動態分配的記憶體,避免記憶體洩漏,但是auto_ptr有很多弊端,下面會通過示例和講解一一將弊端和用法展現出來。 auto_ptr不能初始化為指向非動態記憶體(原因

智慧指標 Auto_Ptr

在介紹智慧指標之前我們先看以下程式碼: void FunTest() { int *p = new int[10]; FILE* pFile = fopen( "1.txt", "w" ); if (pFile == NULL)

c++ 四大智慧指標 std::auto_ptr std::shared_ptr std::unuque std::weak_ptr 比較總結

1. 動態記憶體必要性 程式不知道自己需要多少物件;  程式不知道物件的準確型別;  程式需要在多個物件之間共享資料; 2. 動態記憶體在哪裡 程式有靜態記憶體、棧記憶體。靜態記憶體用來儲存區域性static物件、類static資料成員以及定義在任何函式之外的變數

c++自動釋放的指標——auto_ptr和shared_ptr

假設我們使用一個用來塑模投資行為(例如股票、債券等)的程式庫,其中各式各樣的投資型別整合自一個root class Investment: class Investment { ... };//"投資型別"整合體系中的root class 進一步假設,這個程式庫通過一個工廠模式供應我們

C++智慧指標unique_ptr

從C++智慧指標之auto_ptr一文中得知:在使用auto_ptr時,可能會不經意的將多個auto_ptr指向同一塊記憶體,造成auto_ptr銷燬釋放時多次釋放同一塊記憶體。為了解決該問題,本文引出了unique_ptr。 顧名思義,unique是唯一的意思。說明它跟auto_p

智慧指標 shared_ptr 的使用

一、智慧指標 1. 什麼是智慧指標 簡單地說,C++智慧指標是包含過載運算子的類,其行為像常規指標,但智慧指標能夠及時、妥善地銷燬動態分配的資料,並實現了明確的物件生命週期,因此更有價值。 2. 常規指標存在的問題 C++在記憶體分配、釋放和管理方面向程式猿提供了全面的靈

智慧指標auto_ptr的實現)

舊版的auto_ptr 既然要控制資源的轉移,我們可以多定義一個成員物件_owner來標識該成員物件的資源管理權,在呼叫建構函式建立物件時賦予其資源管理權,即_owner置為true,在呼叫拷貝建構函式、賦值運算子的時候將其管理權釋放,即_owner置為false,就起到了資源管理權的轉

React Native 4 for Android原始碼分析 一《JNI智慧指標介紹篇》

導讀 React Native 釋出以來將近一年多了,也被抄的火爆到不行,包括RN的中文網和各種資料也很多,加之SE5,Se6語法升級,學習成本並不在RN環境搭建和入門,關鍵還是對JS的掌握入門,不管你是用Native開發,h5開發,還是React

C++11智慧指標使用shared_ptr實現多型

指標除了管理記憶體之外,在C++中還有一個重要的功能就是實現多型。 程式碼很簡單,還是使用虛擬函式。與原生指標並沒有什麼區別: #include <iostream> #include &

C++:智慧指標shared_ptr

1.智慧指標 C++中用new來動態分配記憶體,delete手動釋放記憶體來達到動態管理記憶體的目的。因為保證在正確的時間釋放記憶體是非常困難的,忘記釋放記憶體就會產生記憶體洩露。 為了更安全、便捷的使用動態記憶體,C++11標準庫提供了新的智慧指標類來管理

android智慧指標輕量級指標

C++與JAVA的區別是記憶體需要手動管理,具體的來說就是由NEW分配的記憶體或對像需要手動呼叫delete來手動刪除。這樣就導致一個問題,如果大型軟體中一個對像可能在多個執行緒中有使用,必須出現重複多次引用的情況。那麼我們應該在那個執行緒去釋放了,這樣就可能導致釋放了但還有的執行緒在使用導致記憶體洩露,還有

android智慧指標指標和弱指標

LightRefBase是輕量級的,那麼RefBase就應該是重量級的了,它的名字中少了light。Android為神馬要引入這個類呢?想一下這樣一個場景,現在有兩個物件:A和B,物件A中有B的引用,因此B的引用等於1;物件B中有A的引用,因此物件A的引用對於1;現在問題來了,這兩個物件和外界的任何物件都沒有

C++11 智慧指標 std::shared_ptr 初級學習

shared_ptr概念 shared_ptr基於“引用計數”模型實現,多個shared_ptr可指向同一個動態物件,並維護了一個共享的引用計數器,記錄了引用同一物件的shared_ptr例項的數量。當最後一個指向動態物件的shared_ptr銷燬時,會自動

【C++】智慧指標引用計數的實現

在C++11的標準中,引入了智慧指標的概念。 相比於auto_ptr而言,其主要缺陷在於在進行指標拷貝的時候,會出現管理權轉移的問題,導致原指標最終編成一個懸掛指標(dangling pointer

C++智慧指標std::shared_ptr簡單使用和理解

1  智慧指標std::shared_ptr相關知識和如何使用 我們這裡先說下智慧指標std::shared_ptr,因為我看到我我們專案c++程式碼裡面用得很多,我不是不會,所以記錄學習下 先讓ubuntu終端支援c++11,如果自己的電腦還沒配置號,可以先看下我的這篇部落格

C++小知識——記憶體管理智慧指標

在C++中,動態記憶體的管理是通過一對運算子來完成的: 1)new:在動態記憶體中為物件分配空間並返回一個指向該物件的指標 2)delete:接受一個動態物件的指標,銷燬該物件,並釋放與之關聯的記憶體 因為這樣的原因,我們時常很難確保在正確的時間去釋放記憶體,甚至我們忘記了去釋放記憶體,這

C++智慧指標和普通指標單例模式兩種實現

1  問題 實現c++的單例模式,這裡測試分別寫了通過智慧指標返回物件和普通返回指標     2  程式碼測試 include <iostream> #include <mutex> #include <m

c++ 智慧指標:儘量使用std::make_unique和std::make_shared而不直接使用new

關於make_unique的構造及使用例程,MSDN的講解非常詳細 (https://msdn.microsoft.com/zh-cn/library/dn439780.aspx ) 使用過程中,主要有這麼幾個關鍵點: 1.  make_unique 同 uni

C++Boost庫學習智慧指標 shared_ptr

目錄 1.共享指標shared_ptr ^   使用例子 ^ #include<boost/shared_ptr.hpp> using namespace boost; using std::cout; using std::endl; str

C++智慧指標auto_ptr使用講解

auto_ptr是C++98標準庫提供的一個智慧指標,但已被C++11明確宣告不再支援。 auto_ptr具有以下缺陷: auto_ptr有拷貝語義,拷貝後源物件變得無效,這可能引發很嚴重的問題;而unique_ptr則無拷貝語義,但提供了移動語義,這樣的錯誤