讀書筆記:C++中利用智能指標和值型類防止記憶體非法訪問

來源:互聯網
上載者:User

 讀書筆記:C++中利用智能指標和值型類防止記憶體非法訪問
2013-03-10 18:18 by DVwei, 138 閱讀, 0 評論, 收藏, 編輯

在程式當中,經常會用到一些共用對象。一個具有指標成員的類,如果發生複製行為,一個指標複製到另一個指標時,兩個指標就指向同一個對象。此時就可以使用任一指標改變這個共用的對象。那麼,如果一個指標刪除了這個共用對象,那麼另一指標就成了懸垂指標,如果再對此對象進行操作時,就會發生記憶體訪問錯誤。而C++中無法判斷一個指標所指向的記憶體是否有效,這是非常危險的。

看下面一個例子:
複製代碼

class MyClass
{
    public:
        MyClass(int *p,int i): ptr(p),value(i) { }
        ~MyClass() { delete ptr; }
        int get_share_value() { return *ptr; }
        int get_value() { return value; }
        void set_share_value(int i) { *ptr = i; }
        void set_value(int i) { value = i; }
    private:
        int *ptr;
        int value;
};

int main()
{int *p  = new int(10);
    MyClass test(p,20);
    MyClass test2(test);
    test.get_share_value();//return 10
    test2.get_share_value();//return 10
    
    test2.set_share_value(110);
    test.get_share_value();//return 110
    
    test.set_share_value(120);
    test2.get_share_value();//return 120
    
    return 0;
}

複製代碼

可以看到,使用任一指標都能改變共用的對象。而且運行這段代碼就能發現程式結束的時候發生錯誤。這是因為,程式結束時,編譯器會調用test和test2的解構函式,這樣就發生了兩次delete ptr,第一次delete釋放掉val,第二次delete時,指標所指向的記憶體已經無效,所以就會發生記憶體訪問錯誤。

為了避免出現重複delete同一對象的情況,可以定義智能指標來管理共用的對象。所謂智能指標,其實是引入一個使用計數,並建立一個計數類,用來記錄目前指向同一對象的指標數目,確保只剩下最後一個指向對象時才刪除對象。

代碼如下:
複製代碼

#include <iostream>

using namespace std;

class MyClass;

class Use_Ptr
{
    friend class MyClass;
    int *ptr;
    size_t use;
    Use_Ptr(int *p): ptr(p), use(1) { }
    ~Use_Ptr() { delete ptr; }
};

class MyClass
{
    public:
        MyClass(int *p,int i): ptr(new Use_Ptr(p)), value(i) { }

        //發生複製行為時,記錄使用次數
        MyClass(const MyClass &mc): ptr(mc.ptr), value(mc.value) { ++ptr->use; }
        ~MyClass() { if(--ptr->use == 0) delete ptr; }//撤銷對象前檢查使用計數是否為零,若為零,撤銷對象

        MyClass& operator=(const MyClass&);
        int get_share_value() { return *ptr->ptr; }
        int get_value() { return value; }
        void set_share_value(int i) { *ptr->ptr = i; }
        void set_value(int i) { value = i; }
    private:
        Use_Ptr *ptr;
        int value;
};

MyClass& MyClass::operator=(const MyClass &mc)
{
    ++mc.ptr->use;//使用use之前先將sm.ptr->use加一,防止對自身賦值
    if(--ptr->use == 0)
      delete ptr;
    ptr = mc.ptr;

    return *this;
}

int main()
{
    int *p = new int(10);

    MyClass test(p,20);
    MyClass copy(test);
    return 0;
}

複製代碼

這樣就確保了共用的對象在沒有指標指向它的時候才被delete掉,也就不會發生記憶體的非法訪問了。

另一種方法是把類設計成和實值型別具有相同的複製行為,即複製類對象的同時複製指標指向的值,而不是只複製指標的值。於是可以把上述MyClass這樣定義:
複製代碼

class MyClass
{
    public:
        MyClass(int &p,int i): ptr(new int(p)), value(i) { }
        MyClass(const MyClass &mc): ptr(new int(*mc.ptr)), value(mc.value) { }//把值也複製
        ~MyClass() { delete ptr; }//這裡可以直接釋放

        MyClass& operator=(const MyClass&);
        int get_share_value() { return *ptr; }
        int get_value() { return value; }
        void set_share_value(int i) { *ptr = i; }
        void set_value(int i) { value = i; }

    private:
        int *ptr;
        int value;
};

MyClass& MyClass::operator=(const MyClass &mc)
{
    *ptr = *mc.ptr;
    value = mc.value;

    return *this;
}

複製代碼

這樣每個指標都指向一個值相同但相互獨立的對象,delete後也不會影響到其他的對象。

這種方法也叫深度複製,在C#中可以通過實現clone介面來完成。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.