• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            C++分析研究  
            C++
            日歷
            <2014年11月>
            2627282930311
            2345678
            9101112131415
            16171819202122
            23242526272829
            30123456
            統計
            • 隨筆 - 92
            • 文章 - 4
            • 評論 - 4
            • 引用 - 0

            導航

            常用鏈接

            留言簿

            隨筆檔案

            文章檔案

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

             

              常遇到的動態內存回收問題

              在C++的編程過程中,我們經常需要申請一塊動態內存,然后當用完以后將其釋放。通常而言,我們的代碼是這樣的:

              1: void func()

              2: {

              3: //allocate a dynamic memory

              4: int *ptr = new int;

              5:

              6: //use ptr

              7:

              8: //release allocated memory

              9: delete ptr;

              10: ptr = NULL;

              11: }

              如果這個函數func()邏輯比較簡單,問題不大,但是當中間的代碼有可能拋出異常時,上面的代碼就會產生內存泄露(memory leak),如下面代碼中第11行和12行將不會被執行。當然有碼友會說用try-catch包起來就可以了,對,沒錯,但是代碼中到處的try-catch也挺被人詬病的SAT答案 www.sats686.com

              1: void func()

              2: {

              3: //allocate a dynamic memory

              4: int *ptr = new int;

              5:

              6: throw “error”; //just an example

              7:

              8: //use ptr

              9:

              10: //release allocated memory

              11: delete ptr;

              12: ptr = NULL;

              13: }

              而且當函數有多個返回路徑時,需要在每個return前都要調用delete去釋放資源,代碼也會變的不優雅了。

              1: void func()

              2: {

              3: //allocate a dynamic memory

              4: int *ptr = new int;

              5:

              6: if (...)

              7: {

              8: //...a

              9:

              10: //release allocated memory

              11: delete ptr;

              12: ptr = NULL;

              13: return;

              14: } else if (....)

              15: {

              16: //...b

              17:

              18: //release allocated memory

              19: delete ptr;

              20: ptr = NULL;

              21: return;

              22: }

              23:

              24: //use ptr

              25:

              26: //release allocated memory

              27: delete ptr;

              28: ptr = NULL;

              29: }

              鑒于此,我們就要想辦法利用C++的一些語言特性,在函數退棧時能夠將局部申請的動態內存自動釋放掉。熟悉C++的碼友們都知道,當一個對象退出其定義的作用域時,會自動調用它的析構函數。也就是說如果我們在函數內定義一個局部對象,在函數返回前,甚至有異常產生時,這個局部對象的析構函數都會自動調用。如果我們能夠將釋放資源的代碼交付給這個對象的析構函數,我們就可以實現資源的自動回收。這類技術,通常被稱為RAII (初始化中獲取資源)托福答案


              什么是RAII以及幾個例子

              在C++等面向對象語言中,為了管理局部資源的分配以及釋放(resource allocation and deallocation),實現異常安全(exception-safe)、避免內存泄露等問題,C++之父Bjarne Stroustrup發明了一種叫做”初始化中獲取資源“ (RAII, Resource Acquisition Is Initialization,也可以叫做Scope-Bound Resource Management)的技術。簡單來說,它的目的就是利用一個局部對象,在這個對象的構造函數內分配資源,然后在其析構函數內釋放資源。這樣,當這個局部對象退出作用域時,它所對應的的資源即可自動釋放。在實現上,它通常有三個特點:

              創建一個特殊類,在其構造函數初申請資源;

              封裝目標對象,將申請資源的目標對象作為這個特殊類的成員變量;

              在這個類的析構函數內,釋放資源。

              一個典型的例子就是標準庫中提供的模板類std::auto_ptr。如在《C++程序設計語言》(《The C++ Programming Language, Special Edition》, Bjarne Stroustrup著,裘宗燕譯)中第327頁所描述的SAT答案

              1: template

              2: class std::auto_ptr {

              3:

              4: public:

              5: //在構造函數中,獲得目標指針的管理權

              6: explicit auto_ptr(X *p = 0) throw() { ptr = p; }

              7: //在析構函數中,釋放目標指針

              8: ~auto_ptr() throw() { delete ptr; }

              9:

              10: //...

              11:

              12: //重裝*和->運算符,使auto_ptr對象像目標指針ptr一樣使用

              13: X& operator*() const throw() { return *ptr; }

              14: X* operator->() const throw() { return ptr; }

              15:

              16: //放棄對目標指針的管理權

              17: X* release() throw() { X* t = ptr; ptr = 0; return t; }

              18:

              19: private:

              20: X *ptr;

              21: };

              想要使用它,非常簡單,例如

              1: #include

              2:

              3: void func()

              4: {

              5: std::auto_ptr p(new int);

              6:

              7: //use p just like ptr

              8:

              9: return;

              10: }

              另一個例子,是利用GCC中的cleanup attribute。它可以指定一個函數,在該變量退出作用域時可以執行。例如Wikipedia上提到的宏

              1: #define RAII_VARIABLE(vartype,varname,initval,dtor) \

              2: void _dtor_ ## varname (vartype * v) { dtor(*v); } \

              3: vartype varname __attribute__((cleanup(_dtor_ ## varname))) = (initval)

              我們可以這樣使用,例如

              1: void example_usage() {

              2: RAII_VARIABLE(FILE*, logfile, fopen("logfile.txt", "w+"), fclose);

              3: fputs("hello logfile!", logfile);

              4: }

              還有一個例子,是在劉未鵬的博客文章”C++11 (及現代C++風格)和快速迭代式開發“中的”資源管理“一節中看到的,他借助C++11的std::function實現了這一特性。感興趣的碼友可以到他博客內閱讀。

              筆者采用的方法

              對于new/delete,使用上面提到的std::auto_ptr就可以了,但是對于new/delete[]一個動態的一維數組,甚至二維數組,auto_ptr就無能為力了。而且在一些項目中,特別是一些有著悠久歷史的代碼中,還存在著使用malloc, new混用的現象。所以筆者設計了一個auto_free_ptr類,實現目標資源的自動回收。它的實現比較簡單,只利用了RAII的第三個特點——”在類的析構函數內釋放資源”,但有一個優點是可以在申請堆內存代碼前使用托福答案

              代碼如下,

              1: //auto_free_ptr is only used for automation free memory

              2: template

              3: class auto_free_ptr

              4: {

              5: public:

              6: typedef enum {invalid, new_one, new_array, alloc_mem} EFLAG;

              7: auto_free_ptr() { initialize(); }

              8: ~auto_free_ptr(){ free_ptr(); }

              9:

              10: ///set the pointer needed to automatically free

              11: inline void set_ptr(T** new_ptr_address, EFLAG new_eflag)

              12: { free_ptr(); p_ptr = new_ptr_address; eflag = new_eflag; }

              13:

              14: ///give up auto free memory

              15: inline void give_up() { initialize(); }

              16:

              17: protected:

              18: inline void initialize() { p_ptr = NULL; eflag = invalid; }

              19: inline void free_ptr() throw()

              20: {

              21: if(!p_ptr || !(*p_ptr)) return;

              22:

              23: switch(eflag)

              24: {

              25: case alloc_mem: { free(*p_ptr), (*p_ptr) = NULL, p_ptr = NULL; break; }

              26: case new_one: { delete (*p_ptr), (*p_ptr) = NULL, p_ptr = NULL; break; }

              27: case new_array: { delete[] (*p_ptr),(*p_ptr) = NULL, p_ptr = NULL; break; }

              28: }

              29: }

              30:

              31: protected:

              32: T** p_ptr; //!< pointer to the address of the set pointer needed to automatically free

              33: EFLAG eflag; //!< the type of allocation

              34:

              35: private:

              36: DISABLE_COPY_AND_ASSIGN(auto_free_ptr);

              37: };
              為了使用方便,封裝兩個宏:

              1: // auto-free macros are mainly used to free the allocated memory by some local variables in the internal of function-body

              2: #define AUTO_FREE_ENABLE( class, ptrName, ptrType ) \

              3: auto_free_ptr auto_free_##ptrName; \

              4: auto_free_##ptrName.set_ptr(&ptrName,auto_free_ptr::ptrType)

              5:

              6: #define AUTO_FREE_DISABLE( ptrName ) auto_free_##ptrName.give_up()

              使用起來很簡單,例如

              1: void func(int nLftCnt, int nRhtCnt)

              2: {

              3: if (!nLftCnt && !nRhtCnt)

              4: return;

              5:

              6: unsigned *pLftHashs = NULL;

              7: unsigned *pRhtHashs = NULL;

              8:

              9: //在申請堆內存之前,使用auto_free_ptr

              10: AUTO_FREE_ENABLE(unsigned, pLftHashs, new_array);

              11: AUTO_FREE_ENABLE(unsigned, pRhtHashs, new_array);

              12:

              13: //....

              14:

              15: if (nLftCnt)

              16: {

              17: pLftHashs = new unsigned[nLftCnt];

              18: //...a

              19: }

              20:

              21: if (nRhtCnt)

              22: {

              23: pRhtHashs = new unsigned[nRhtCnt];

              24: //...b

              25: }

              26:

              27: //....

              28:

              29: if (...)

              30: {

              31: //因為下面這個函數可以釋放資源,所以在它前面放棄對目標指針的管理權

              32: AUTO_FREE_DISABLE(pLftHashs);

              33: AUTO_FREE_DISABLE(pRhtHashs);

              34:

              35: //這個函數可以釋放資源

              36: free_hash_arrays(pLftHashs, pRhtHashs);

              37: }

              38: }

              同樣的,有時我們需要申請一個動態二維數組,所以也實現一個對應的auto_free_2D_ptr

              1: //auto_free_2D_ptr is only used for automation free memory of 2D array

              2: template

              3: class auto_free_2D_ptr

              4: {

              5: public:

              6: typedef enum {invalid, new_one, new_array, alloc_mem} EFLAG;

              7: auto_free_2D_ptr() { initialize(); }

              8: ~auto_free_2D_ptr() { free_ptr(); }

              9:

              10: ///set the pointer needed to automatically free

              11: inline void set_ptr( T** new_ptr_address,EFLAG new_eflag, int new_length_row )

              12: { free_ptr(); p_ptr = new_ptr_address; eflag = new_eflag; length_row = new_length_row; }

              13:

              14: //give up auto free memory

              15: inline void give_up() { initialize(); }

              16:

              17: protected:

              18: inline void initialize() { p_ptr = NULL; eflag = invalid; length_row = 0;}

              19: inline void free_ptr() throw()

              20: {

              21: if(!p_ptr || !(*p_ptr)) return;

              22:

              23: for(int i = 0; i < length_row; i++)

              24: {

              25: if(!(*p_ptr)[i]) continue;

              26: switch(eflag)

              27: {

              28: case alloc_mem: { free((*p_ptr)[i]); break; }

              29: case new_one: { delete (*p_ptr)[i]; break; }

              30: case new_array: { delete[] (*p_ptr)[i]; break; }

              31: }

              32: (*p_ptr)[i] = NULL;

              33: }

              34: switch(eflag)

              35: {

              36: case alloc_mem: { free((*p_ptr)); break; }

              37: default: { delete[] (*p_ptr); break; }

              38: }

              39: (*p_ptr) = NULL, p_ptr = NULL;

              40: }

              41:

              42: protected:

              43: T** p_ptr; //!< pointer to the address of the set pointer needed to automatically free

              44: EFLAG eflag; //!< the type of allocation

              45: int length_row; //!< the row length such as ptr[length_row][length_col]

              46:

              47: private:

              48: DISABLE_COPY_AND_ASSIGN(auto_free_2D_ptr);

              49: };

              50:

              51: #define AUTO_FREE_2D_ENABLE( class, ptrName, ptrType, rowNum ) \

              52: auto_free_2D_ptr auto_free_##ptrName; \

              53: auto_free_##ptrName.set_ptr(&ptrName,auto_free_2D_ptr::ptrType, rowNum)

              54:

              55: #define AUTO_FREE_2D_DISABLE( ptrName ) AUTO_FREE_DISABLE( ptrName )

              下面是個例子

              1: void func(int row, int col)

              2: {

              3: if (!row && !col)

              4: return;

              5:

              6: int **ptr = new int*[ row ];

              7: for( int r = 0; r < row; ++r ) { ptr[r] = new int[ col ];}

              8:

              9: AUTO_FREE_2D_ENABLE( int, ptr, new_array, row );

              10:

              11: //....

              12: }

              到這里就結束了,有些碼友可能會說,何必這么麻煩,boost內有很多智能指針供選擇,用share_ptr, scoped_ptr, scoped_array,unique_ptr, auto_ptr 中的一個不就行了嗎? 沒錯!如果你正在開發的代碼中,允許用boost,并且在相關程序接口統一都用智能指針來管理、不會用到源對象指針的話,當然優先選boost,但是當你的代碼中由于歷史原因,有些接口不可變更,且new/delete, malloc/free都存在,而且依然需要使用源對象指針來完成大部分工作時,不妨試試我設計的這個閹割版的scoped_ptr/scoped_array??傊?,根據自己的實際情況來選擇合適的方案,如果標準方案不適用,就自己寫一個托福答案

            posted on 2014-11-16 09:05 HAOSOLA 閱讀(692) 評論(0)  編輯 收藏 引用
             
            Copyright © HAOSOLA Powered by: 博客園 模板提供:滬江博客
            PK10開獎 PK10開獎
            女同久久| 婷婷综合久久中文字幕| 久久99热国产这有精品| 久久国产精品免费一区二区三区| 精品伊人久久久| 亚洲乱码精品久久久久..| 久久久亚洲欧洲日产国码aⅴ| 99久久国产主播综合精品| 久久久久国色AV免费看图片| 香蕉久久久久久狠狠色| 久久久久四虎国产精品| 久久亚洲日韩精品一区二区三区| 久久受www免费人成_看片中文| 性色欲网站人妻丰满中文久久不卡| 青青青伊人色综合久久| 热re99久久精品国99热| 四虎国产精品成人免费久久| 91久久福利国产成人精品| 伊人久久大香线蕉亚洲| 亚洲人成电影网站久久| 久久99免费视频| 久久久久国产精品熟女影院| 亚洲国产一成久久精品国产成人综合| 93精91精品国产综合久久香蕉| 亚洲国产精品无码久久一线| 亚洲精品NV久久久久久久久久 | 亚洲精品高清久久| 少妇高潮惨叫久久久久久| 亚洲精品久久久www| 欧美日韩精品久久久久| 久久成人永久免费播放| 精品久久久久久无码中文野结衣| 国产V综合V亚洲欧美久久| 色综合久久最新中文字幕| 久久精品亚洲日本波多野结衣| 日韩人妻无码精品久久免费一| 亚洲色大成网站WWW久久九九| 欧美熟妇另类久久久久久不卡 | 97久久综合精品久久久综合| 99久久国产亚洲高清观看2024 | 国产69精品久久久久APP下载 |