內存管理之智能指針一:stl中的auto_ptr智能指針

 一、智能指針作爲一種內存管理技術主要爲了解決什麼問題?(屬於個人理解)

1、一般在應用軟件的開發過程中,應用程序的代碼執行基本上都是通過以下幾個步驟來完成:

(1)獲取資源(內存、文件句柄、數據庫連接等);

(2)執行功能代碼;

(3)釋放資源;

可能資源的獲取相對而言是一個比較容易處理的過程,以內存爲例,通過new操作符即可進行內存資源的獲得,但是在應用實現的過程中,可能程序員不能保證資源的正確釋放,甚至是忘記了調用delete或者是delete[]操作符,導致內存的泄露;有時候也是我們自己無法控制,譬如代碼在運行過程中產生異常,退出,那麼分配的資源也是無法釋放,針對以上種種情況,爲了解決資源的正確獲取和釋放,提出了智能指針來解決該種問題!

可能有時候會利用智能指針來保證線程安全等方面實踐!

二、智能指針利用到的技術思想是什麼?

(1)利用面嚮對象的技術,在一個obj的生存期結束的時候會自動調用自己的析構函數,實現內存的資源釋放

(2)利用GOF中的proxy(代理模式)的設計思想,利用一個obj來proxy指針的功能,實現和指針完全相同的操作(一般不包括指針的算術運算操作)

因此被稱爲智能指針,其實在實現層面上更本就不是一個“指針”(pointer),而是一個代理對象,重載了相關的->和*操作符,行爲上類似指針。

 

三、STL中的智能指針auto_ptr(釋放所有權的智能指針)

1、所在頭文件<memory>,內存管理

2、auto_ptr指針概述

auto_ptr是stl(c98標準)庫中的唯一一個標準智能指針,主要的目的就是避免相關資源泄露,保證代碼的異常安全。

特點是:auot_ptr代理的資源始終都只屬於唯一的一個auto_ptr,不能實現資源的共享,即使是在auto_ptr之間進行資源的傳遞也是如此,因此被稱爲“釋放所有權”。

正式這個特點也是它最主要的一個缺點,雖然可以對資源進行正確的管理,但是對應用人員的要求就比較高了,需要應用人員對這個智能指針的對象的特性比較瞭解,在使用的過程中,記住相關的特性,才能正確使用,避免出現一些“低級錯誤”代碼。

缺點:在實現上與常規的特性違背了,特別是對於copy construct和assign的操作,在理念上和我們的思想違背了,因爲它改變了被拷貝和被賦值者得屬性,因此在使用的時候我們必須要把握住它的這個特點,那麼纔可以正確的使用它。


使用的時候需要注意如下幾點:
(1)auto_ptr不能共享所有權
因此在使用auto_ptr的時候不能讓兩個auto_ptr同時擁有一個資源的指針,那麼就會出現資源的重複釋放,出現系統級的錯誤!在這裏需要提出一點:最好不要對auto_ptr進行“裸指針”的操作,即使用get()方法,因爲這樣很危險,代碼無法保證,只能由程序員自己去保證。

(2)不存在處理array的auto_ptr指針
因爲auto_ptr在析構的時候是使用delete,而不是delete[]


(3)auto_ptr不能滿足stl對元素的要求,這個應該是它的一個硬傷
因爲auto_ptr的所有權轉移的特點,使得它無法滿足container的要求在stl中爲了滿足功能性,在進行元素的操作都要求元素提供copy construct和assign,但是在容器中很多時候都是通過value拷貝的,很少有ref,並且參數大部分都是const ref,因此在對auto_ptr執行copy的時候,滿足c98的complier應該都會報錯,否則你就慘了。


(4)對於auto_ptr的使用,如果僅僅只是爲了保證資源的正確獲取和釋放
最好使用const auto_ptr obj,這樣在進行相互賦值的時候,complier會幫你檢查錯誤。const修飾的對象是obj,只是說自己的所有權是不允許被轉移,這屬於一種obj feature,因此obj proxy的pointer的value不能改變,但是*pointer的value是可以改變的。

 

(5)在auto_ptr中引入了auto_ptr_ref輔助類,主要是爲了解決const的變量作爲右值如何變爲左值的一個小技巧

大家應該都知道,對於一個函數如果返回一個obj對象,如果不是引用對象,那麼就會產生一個臨時變臉,在c++標準中對於零時變量,規定爲const的右值,但是如果把一個返回auto_ptr的零時變量的函數作爲右值,並且賦值給一個auto_ptr變量,例如:

auto_ptr a = function()< return auto_ptr(new int(11))>

因爲auto_ptr的copy construct的特性使得它無法使用const auto_ptr作爲入參,因此爲了解決這個問題,引入了上面的輔助類,在模板技術中重載了相關的類型轉換操作符,提供了auto_ptr_ref到auto_ptr的一個construct,這樣就通過這種技術實現了const auto_ptr的臨時變量到auto_ptr的對象值之間的“擁有權釋放”的問題。

 

對於相關技術可以參考stl中的源碼!

代碼示例說明:

#include <iostream>
#include <memory>

int main( void )
{
    // 創建一個auto_ptr<int> 對象
    std::auto_ptr<int> ptr( new int(11) );
    // 通過重載*操作符實現value提取操作
    if ( ptr.get() )
        std::cout << *ptr << std::endl;

    // 創建另一個對象,實現所有權的轉移
    std::auto_ptr<int> cpptr = ptr;
    // 判斷所有權是否轉移
    if (ptr.get())
        std::cout << "*ptr value = " << *ptr << std::endl;

    if (cpptr.get())
        std::cout << "*cpptr value = " << *cpptr << std::endl;

    return 0;
};

輸出結果:可以看到ptr釋放了自己的所有權

在實現的過程中利用new操作符開闢了一個新的空間,但是沒有使用delete操作符,並不是忘記了

這個正是隻能指針的作用,可能通過內置類型無法看到效果,可以自己寫一個類,然後再析構函數中輸出相關的打印信息,就可以得到相關的驗證。

 

關於auto_ptr錯誤使用的一個例子:

#include <iostream>
#include <memory>

void function( std::auto_ptr<int> ptr )
{
    std::cout << *ptr << std::endl;
}

int main( void )
{
    // 創建一個auto_ptr<int> 對象
    std::auto_ptr<int> ptr( new int(11) );
    // 通過重載*操作符實現value提取操作
    if ( ptr.get() )
        std::cout << *ptr << std::endl;

    function( ptr );

    // 這個會報錯,因爲auto_ptr在使用的過程中進行了所有權的轉移
    // 因此這個new的資源已經在function()函數中釋放了,ptr中擁有的
    // 是一個空指針,這個會在運行時報錯,如果不報錯你就慘了!
    // 應該該寫如下:
   
//     if ( ptr.get() )
//         *ptr = 20;
    *ptr = 20;

    return 0;
};

因此在這種情況下只能程序員自己去保證了,complier無法保證這種錯誤。

其實現在很少使用auto_ptr指針,而主要是使用boost庫中的五種智能指針!

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章