程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 如何養成良好的C++編程習慣(一)—— 內存管理

如何養成良好的C++編程習慣(一)—— 內存管理

編輯:C++入門知識

開篇導讀
  “養成良好的編程習慣”其實是相當綜合的一個命題,可以從多個角度、維度和層次進行論述和評判。如代碼的風格、效率和可讀性;模塊設計的靈活性、可擴展性和耦合度等等。要試圖把所有方面都闡述清楚必須花很多的精力,而且也不一定能闡述得全面。因此,本系列文章以軟件開發的基礎問題為切入點,闡述程序設計和代碼編寫方面的細節問題,以點帶面,旨在激發大家的思考與總結,希望能為大家帶來實際的幫助。         
  雖然本系列文章定位為科普讀物,但本座相信它們不但適合新手們學習借鑒,同時也能引發老鳥們的反思與共鳴。歡迎大家提出寶貴的意見和反饋 ^_^
  在開篇講述本章主要內容之前,本座首先用小小篇幅論述一下一種良好的工作習慣 —— 積累、提煉與求精。在工作和學習的過程中,不斷把學到的知識通過有效的方式積累起來,形成自己的知識庫,隨著知識量的擴大,就會得到從量變到質變的提升。另外還要不斷地對知識進行提煉,隨著自己知識面的擴大以及水平的提升,你肯定會發現原有知識庫存在著一些片面、局限、笨拙甚至錯誤。這時,就需要你有精益求精精的態度和毅力對知識庫進行優化整理。
  也許以上這些各位都曾想過去實施,也明白其中的道理,但是自己就是給自己各種堂而皇之的借口不花時間去做。這樣說吧,技術之路不好走,這個行業有兩項基本要求:1、對軟件開發工作本身有很大興趣;2、耐得住寂寞。兩者缺一不可,否則還是趁年輕早點轉行吧,要不轉做軟件行業的銷售、產品或者管理也行,總之就不要做開發 ^_^
________________________________________
 
內存管理相關問題
  一提起 C/C++ 的內存管理,大部分人腦海裡都立刻湧出 new / delete / malloc / free 等幾個恐怖的單詞吧?的確,C/C++ 的手工內存管理是它們區別於其他語言的一大特點,也像一道屏障立在那些想從其它語言轉向 C/C++ 的人士身前。由此也引起各大論壇對 “C++ 人氣低落”和“是否應該引入垃圾回收機制”等相關話題的劇烈爭論。本座一直無視這些爭論,其實並非本座不關心 C++ 的發展與命運,相反,本座十分關心。雖然從現在的眼光看來,無論是 C++ 身上有多少硬傷,C++ 委員會的大爺們和 C++ 編譯器廠商的大佬們如何扯貓尾。畢竟最愛就是最愛,殘缺美也是美,不解釋。本座之所以不關心這些爭論,原因是因為看透了,一門語言就像一種人生,是有生命周期的,沒落只是快慢的問題,舊事物總會被新事物取代,這是客觀規律不可避免。秦始皇最終不也是沒找到長生不老的仙丹麼?只要曾經發光發熱過,在還有價值的時候能為大眾所用就已經無憾了。本座在此還要申明一種態度:本座並不排斥任何語言,相反,本座對新語言的誕生非常感興趣。會去了解它們的特點,看看它們能幫助解決哪方面的問題。正如這幾年,由於工作需要,本座用得最多的是 Java 和一些動態語言(它們的確能解決很多問題),而 C/C++ 卻沒再用了。
  嗯,扯遠了,我們還是回到正題吧。說起 C/C++ 的內存管理似乎令人望而生畏,滿屏的 new / delete / malloc / free,OutPut 窗口無盡的 Memory Leak 警告,程序詭異的 0X00000004 指針異常,仿佛回到那一年我們一起哭過的日子,你 Hold 得住嗎?其實,現實並沒有你想的那麼糟糕。只要你付出一點點,花一點點心思,沒錯!就一點點而已 —— 用 C++ 類封裝內存訪問,就會解決你大部分的煩惱,讓你受益終身。以 Windows 程序為例,主要有以下幾種內存管理方式:
• 虛擬內存(Virtual Memory)
• 默認堆和私有堆(Process Heap & Private Heap)
• 內存映射文件(File Mapping)
• 進程堆棧(Heap,其實就是用 malloc() 或 默認的 new 操作符在 Process Heap 裡一小塊一小塊地割肉 ^_^)
• 棧(Stack,內存由調用者或被調用者自動管理)
   今天我們的主題是封裝,至於每種內存模型的概念和 API 的使用方式在這裡就不講了,Google 一下就知道。其實用 C++ 封裝上述前 4 種內存訪問的原理都差不多,就是在構造函數或其他操作函數中分配內存,然後再在析構函數中確保內存被正確釋放。虛擬內存、默認堆和私有堆的操作方式相似,這裡就不一一展示了,有興趣的朋友可以參考本座前幾天發表的那篇無人問津的文章:《C++ 封裝私有堆(Private Heap)》,哎!下面對內存映射文件的封裝也只稍作介紹、我們主要討論的是使用頻率最高的 malloc() 和 new 的封裝。
________________________________________
 
 內存映射文件
  下面的代碼把 File Mapping 句柄以及從 File Mapping 映射出來的內存分別封裝到 CFileMapping 和 CShareMemory 中,可以直接使用 CShareMemory 可以創建一個 File Mapping 以及映射 File Mapping 的內存。
 
class CFileMapping
{
public:
    CFileMapping(   
                    LPCTSTR lpszName,
                    DWORD dwMaximumSizeLow,
                    DWORD dwMaximumSizeHigh                            = 0,
                    HANDLE hFile                                    = INVALID_HANDLE_VALUE,
                    DWORD flProtect                                    = PAGE_READWRITE,
                    LPSECURITY_ATTRIBUTES lpFileMappingAttributes    = NULL
                )
    {
        m_hMap    = ::CreateFileMapping    (
                                            hFile,
                                            lpFileMappingAttributes,
                                            flProtect,
                                            dwMaximumSizeHigh,
                                            dwMaximumSizeLow,
                                            lpszName
                                        );
        ASSERT(IsValid());
    }

    ~CFileMapping()
    {
        if(IsValid())
            VERIFY(::CloseHandle(m_hMap));
    }

    LPVOID ViewMap    (
                        DWORD dwNumberOfBytesToMap,
                        DWORD dwFileOffsetLow,
                        DWORD dwFileOffsetHigh    = 0,
                        DWORD dwDesiredAccess    = FILE_MAP_ALL_ACCESS
                    )
    {
        return ::MapViewOfFile    (
                                    m_hMap,
                                    dwDesiredAccess,
                                    dwFileOffsetHigh,
                                    dwFileOffsetLow,
                                    dwNumberOfBytesToMap
                                );
    }

    BOOL UnViewMap(LPCVOID lpBaseAddress)
    {
        return ::UnmapViewOfFile(lpBaseAddress);
    }

    operator HANDLE    ()    {return m_hMap;}
    BOOL IsValid    ()    {return m_hMap != NULL;}

private:
    HANDLE m_hMap;
   
    DECLARE_PRIVATE_COPY_CONSTRUCTOR(CFileMapping)
};

class CShareMemory
{
public:
    CShareMemory(DWORD dwSize, LPCTSTR lpszName = NULL)
    : m_fm(lpszName, dwSize)
    {
        ASSERT(dwSize > 0);
    }

    ~CShareMemory()
    {
        for(set<ULONG_PTR>::const_iterator it = m_set.begin(); it != m_set.end(); ++it)
        {
            LPVOID pV = (LPVOID)*it;
            ASSERT(pV);

            m_fm.UnViewMap(pV);
        }

        m_set.clear();
    }

    LPVOID Alloc(DWORD dwNumberOfBytesToMap, DWORD dwFileOffsetLow)
    {
        LPVOID pV = m_fm.ViewMap(dwNumberOfBytesToMap, dwFileOffsetLow);

        if(pV) m_set.insert((ULONG_PTR)pV);

        ASSERT(pV);
        return pV;
    }

    BOOL Free(LPCVOID lpBaseAddress)
    {
        ASSERT(lpBaseAddress);

        set<ULONG_PTR>::iterator it = m_set.find((ULONG_PTR)lpBaseAddress);

        if(it != m_set.end())
            m_set.erase(it);

        return m_fm.UnViewMap(lpBaseAddress);
    }

private:

    CFileMapping    m_fm;
    set<ULONG_PTR>    m_set;

    DECLARE_PRIVATE_COPY_CONSTRUCTOR(CShareMemory)
};
 
  細心的朋友一定會發覺其實這樣封裝是有缺點的:首先,CShareMemory 只能做內存共享,不能映射到真實文件(hFile 永遠為 INVALID_HANDLE_VALUE);第二,可以對 CShareMemory 的 Alloc() 和 Free() 方法進一步封裝,利用封裝類的析構函數自動調用 Free(),這樣就可以完全消除 “set<ULONG_PTR> m_set” 這個屬性了;第三,CFileMapping 也可以把文件句柄一起封裝進來,這樣,從 CreateFile() 到 CreateFileMapping() 都受控了。這個不完美的封裝就權當反面教材吧 ^_^
 
________________________________________
 
malloc() 系列函數
  很多人都建議,在 C++ 中盡量用 new 操作符取代 malloc(),因為 new 類型安全,自動調用構造函數和析構函數等等。關於這點本座略有異議,在某些情形下 malloc() 其實比 new 更好使,效率方面我們可以不計較(幾乎所有編譯器的 new 操作符都用 malloc() 分配內存),從事過偏底層開發的人都清楚,我們避免不了處理 row data(如:socket 的收發緩沖區等)數據,這類數據是非常適合使用 malloc() 的,用 new 分配的內存還要停頓下來想想到底是用 delete、delete[]、::delete、::delete[] 中的哪個釋放,malloc() 分配的內存想都不用想,free() 包打天下,何況人家有 realloc() 可以方便地重新調整內存,你有沒有 “renew” 呢?總之一句話,malloc() 的確是有存在的必要,就看接下來我們如何封裝它了,請看代碼:
 
// T                 : 數據類型(內置類型或結構體)
// MAX_CACHE_SIZE    : 預申請內存的最大數目,以 sizeof(T) 為單位,如果該值設置合理,對於
//                     需要動態遞增緩沖區的 buffer 來說能大大提高效率
template<class T, size_t MAX_CACHE_SIZE = 0>
class CBufferPtrT
{
public:
    explicit CBufferPtrT(size_t size = 0, bool zero = false)    {Reset(); Malloc(size, zero);}
    explicit CBufferPtrT(const T* pch, size_t size)    {Reset(); Copy(pch, size);}
    // 拷貝構造函數要分兩種情形
    CBufferPtrT(const CBufferPtrT& other)    {Reset(); Copy(other);}
    template<size_t S> CBufferPtrT(const CBufferPtrT<T, S>& other)    {Reset(); Copy(other);}

    ~CBufferPtrT() {Free();}

    T* Malloc(size_t size = 1, bool zero = false)
    {
        Free();
        return Alloc(size, zero, false);
    }

    T* Realloc(size_t size, bool zero = false)
    {
        return Alloc(size, zero, true);
    }

    void Free()
    {
        if(m_pch)
        {
            free(m_pch);
            Reset();
        }
    }

    template<size_t S> CBufferPtrT& Copy(const CBufferPtrT<T, S>& other)
    {
        if((void*)&other != (void*)this)
            Copy(other.Ptr(), other.Size());

        return *this;
    }

    CBufferPtrT& Copy(const T* pch, size_t size)
    {
        Malloc(size);

        if(m_pch)
            memcpy(m_pch, pch, size * sizeof(T));

        return *this;
    }

    // 動態擴大 buffer
    template<size_t S> CBufferPtrT& Cat(const CBufferPtrT<T, S>& other)
    {
        if((void*)&other != (void*)this)
            Cat(other.Ptr(), other.Size());

        return *this;
    }

    // 動態擴大 buffer
    CBufferPtrT& Cat(const T* pch, size_t size = 1)
    {
        size_t pre_size = m_size;
        Realloc(m_size + size);

        if(m_pch)
            memcpy(m_pch + pre_size, pch, size * sizeof(T));

        return *this;
    }

    template<size_t S> bool Equal(const CBufferPtrT<T, S>& other) const
    {
        if((void*)&other == (void*)this)
            return true;
        else if(m_size != other.Size())
            return false;
        else if(m_size == 0)
            return true;
        else
            return (memcmp(m_pch, other.Ptr(), m_size * sizeof(T)) == 0);
    }

    bool Equal(T* pch) const
    {
        if(m_pch == pch)
            return true;
        else if(!m_pch || !pch)
            return false;
        else
            return (memcmp(m_pch, pch, m_size * sizeof(T)) == 0);
    }

    T*    Ptr()    {return m_pch;}
    const T*    Ptr()    const    {return m_pch;}
    T&    Get(int i)    {return *(m_pch + i);}
    const T&    Get(int i)    const    {return *(m_pch + i);}
    size_t    Size()    const    {return m_size;}
    bool    IsValid()    const    {return m_pch != 0;}
    // 啊哈,竟然是類型安全的
    operator    T*    ()    {return Ptr();}
    operator const    T*    ()    const    {return Ptr();}
    // 哇塞,竟然還支持索引訪問
    T& operator    []    (int i)    {return Get(i);}
    const T& operator    []    (int i)    const    {return Get(i);}
    bool operator    ==    (T* pv)    const    {return Equal(pv);}
    template<size_t S> bool operator    ==    (const CBufferPtrT<T, S>& other)    {return Equal(other);}
    // 賦值操作符要分兩種情形
    CBufferPtrT& operator    =    (const CBufferPtrT& other)    {return Copy(other);}
    template<size_t S> CBufferPtrT& operator    =    (const CBufferPtrT<T, S>& other)    {return Copy(other);}

private:
    void Reset()    {m_pch = 0; m_size = 0; m_capacity = 0;}
    size_t GetAllocSize(size_t size)    {return max(size, min(size * 2, m_size + MAX_CACHE_SIZE));}

    T* Alloc(size_t size, bool zero = false, bool is_realloc = false)
    {
        if(size >= 0 && size != m_size)
        {
            size_t rsize = GetAllocSize(size);
            if(size > m_capacity || rsize < m_size)
            {
                m_pch = is_realloc                            ?
                    (T*)realloc(m_pch, rsize * sizeof(T))    :
                   (T*)malloc(rsize * sizeof(T))            ;

                if(m_pch || rsize == 0)
                {
                    m_size        = size;
                    m_capacity    = rsize;
                }
                else
                    Reset();
            }
            else
                m_size = size;
        }

        if(zero && m_pch)
            memset(m_pch, 0, m_size * sizeof(T));

        return m_pch;
    }

private:
    T*        m_pch;
    size_t    m_size;
    size_t    m_capacity;
};

// 常用 buffer 類型的 typedef
typedef CBufferPtrT<char>            CCharBufferPtr;
typedef CBufferPtrT<wchar_t>        CWCharBufferPtr;
typedef CBufferPtrT<unsigned char>    CByteBufferPtr;
typedef CByteBufferPtr                CBufferPtr;

#ifdef _UNICODE
    typedef CWCharBufferPtr            CTCharBufferPtr;
#else
    typedef CCharBufferPtr            CTCharBufferPtr;
#endif

  嗯。這裡要解釋一下為何需要兩個拷貝構造函數和賦值操作符重載,首先,編譯器為不同的模板參數生成不同的類,也就是說:CBufferPtrT<int, 1> 和 CBufferPtrT<int, 2> 被看作是不同的類,另外,C++ 編譯器為每個類提供了提供了拷貝構造函數和賦值操作符重載的默認實現(淺拷貝)。因此,上述的第一組拷貝構造函數和賦值操作符重載是改寫編譯器的默認實現,第二組拷貝構造函數和賦值操作符重載是處理其它類到本類的轉換。
  本座對這個封裝灰常滿意(唯一美中不足的就是 cnblogs 的編輯器太坑爹了,把代碼弄亂 ^_^),它並非只是一個普通的 malloc() 封裝,完全能可以把它看作是一種“支持索引訪問的類型安全的動態緩沖區”。如果把它放在一個 socket 通信類中作為成員屬性,充當跨越多個線程和多個方法訪問的接收緩沖區和發送緩沖區的角色就最適合不過了(當然要自己做同步了)。大家可以調試一下下面的測試例子,了解一下它的用法:
 
  測試用例
 1 int _tmain(int argc, _TCHAR* argv[])
 2 {
 3     CBufferPtr buffer;
 4
 5     unsigned char c1    = 'X';
 6     unsigned char pc1[] = "123";
 7     unsigned char pc2[] = "abc";
 8     buffer.Cat(&c1);
 9     buffer.Cat(pc1, 3);
10     buffer.Cat(pc2, 3);
11
12     CBufferPtrT<unsigned char, 10> buffer2 = buffer;
13     buffer2.Cat(buffer);
14     buffer2.Realloc(0);
15
16     unsigned char* pc = buffer;
17     const unsigned char& c = buffer[5];
18     buffer[5] = 'O';
19
20     short i1    = 0x7FFF;
21     short pi0[] = {9,9,9};
22     short pi1[] = {1,2,3};
23     short pi2[] = {4,5,6};
24     short pi3[] = {8,8,8};
25
26     CBufferPtrT<short, 10> bufferS(pi0, 3);
27
28     bufferS.Cat(&i1);
29     bufferS.Cat(pi1, 3);
30     bufferS.Cat(pi2, 3);
31     bufferS.Cat(pi3, 3);
32
33     CBufferPtrT<short, 5> bufferS2;
34     bufferS2.Malloc(4);
35
36     bufferS2 = bufferS;
37     bufferS2.Realloc(30);
38
39     CBufferPtrT<int> bufferI(5, true);
40    
41     for(size_t i = 0; i < bufferI.Size(); i++)
42         bufferI[i] = i *10;
43
44     bufferI.Malloc();
45     bufferI[0] = 123;
46
47     // 下面這行編譯不通過,正好說明這個類是類型安全的
48 // bufferI = bufferS;
49
50     return 0;
51 }
 
________________________________________
 
new & delete
  一說到 new 的封裝大家立馬想到的就是智能指針吧!沒錯,就是智能指針。但 STL 提供的 auto_ptr 缺陷很多,首先使用起來不方便,竟然連這種寫法都不支持:“std::auto_ptr<int> pi = new int;”,天理何在啊!更可恨的是不支持數組指針(需要 delete[]),另外如果某些類重載了 new 操作符的話使用它也有很多問題的,還有其它的很多缺點(我忘記了 ^_^)。不過,C++0x 似乎對智能指針作了重大改進,已經有支持引用計數的智能指針了,但不知是否解決數組指針和區分 delete 與 ::delete 的問題(本座沒實測,要是您知道麻煩告訴一聲 ^_^)。無論如何,下面代碼列出的智能指針支持區分 delete / delete[] / ::delete / ::delete[]。算是 auto_ptr 的改良(也沒有使用引用計數),文章篇幅太長了,測試用例就不發了,各位看官自行嘗試吧:
/************************************************************************/
/*                       smart_ptr 單實體或數組智能指針                    */
/************************************************************************/

template<class _Ty>
struct simple_deleter
{
    static void delete_ptr(_Ty* pv) {delete pv;}
};

template<class _Ty>
struct global_simple_deleter
{
    static void delete_ptr(_Ty* pv) {::delete pv;}
};

template<class _Ty>
struct array_deleter
{
    static void delete_ptr(_Ty* pv) {delete[] pv;}
};

template<class _Ty>
struct global_array_deleter
{
    static void delete_ptr(_Ty* pv) {::delete[] pv;}
};

template<class _Ty, class _Deleter>
class smart_ptr
{
public:
    smart_ptr(_Ty* _Ptr = 0)                    : _Myptr(_Ptr)                {}
    smart_ptr(smart_ptr<_Ty, _Deleter>& _Right)    : _Myptr(_Right.release())    {}

    ~smart_ptr()
    {
        reset();
    }

    smart_ptr<_Ty, _Deleter>& reset(_Ty* _Ptr = 0)
    {
        if (_Ptr != _Myptr)
        {
            if(_Myptr)
                _Deleter::delete_ptr(_Myptr);

            _Myptr = _Ptr;
        }

        return *this;
    }

    smart_ptr<_Ty, _Deleter>& reset(smart_ptr<_Ty, _Deleter>& _Right)
    {
        if (this != &_Right)
            reset(_Right.release());

        return *this;
    }

    _Ty* release()
    {
        _Ty* _Ptr    = _Myptr;
        _Myptr        = 0;

        return _Ptr;
    }

    smart_ptr<_Ty, _Deleter>& operator = (_Ty* _Ptr)                        {return reset(_Ptr);}
    smart_ptr<_Ty, _Deleter>& operator = (smart_ptr<_Ty, _Deleter>& _Right)    {return reset(_Right);}

    bool is_valid        ()    const    {return _Myptr != 0;}
    _Ty& operator *        ()    const    {return *_Myptr;}
    _Ty* get            ()    const    {return _Myptr;}
    _Ty* operator ->    ()    const    {return _Myptr;}
    operator _Ty*        ()    const    {return _Myptr;}

private:
    template<class _Other> smart_ptr<_Ty, _Deleter>                    (const smart_ptr<_Ty, _Other>&);
    template<class _Other> smart_ptr<_Ty, _Deleter>&    reset        (const smart_ptr<_Ty, _Other>&);
    template<class _Other> smart_ptr<_Ty, _Deleter>&    operator =    (const smart_ptr<_Ty, _Other>&);

    template<class _Other> smart_ptr<_Ty, _Deleter>                    (const smart_ptr<_Other, _Deleter>&);
    template<class _Other> smart_ptr<_Ty, _Deleter>&    reset        (const smart_ptr<_Other, _Deleter>&);
    template<class _Other> smart_ptr<_Ty, _Deleter>&    operator =    (const smart_ptr<_Other, _Deleter>&);

protected:
    _Ty* _Myptr;
};


/************************************************************************/
/*                    smart_simple_ptr 單實體智能指針                      */
/************************************************************************/

template<class _Ty>
class smart_simple_ptr : public smart_ptr<_Ty, simple_deleter<_Ty>>
{
public:
    smart_simple_ptr(_Ty* _Ptr = 0)                                    : smart_ptr(_Ptr)    {}
    smart_simple_ptr(smart_simple_ptr<_Ty>& _Right)                    : smart_ptr(_Right)    {}
    smart_simple_ptr(smart_ptr<_Ty, simple_deleter<_Ty>>& _Right)    : smart_ptr(_Right)    {}

    smart_simple_ptr<_Ty>& operator = (smart_ptr<_Ty, simple_deleter<_Ty>>& _Right)
    {return (smart_simple_ptr<_Ty>&)__super::operator = (_Right);}

    smart_simple_ptr<_Ty>& operator = (smart_simple_ptr<_Ty>& _Right)
    {return (smart_simple_ptr<_Ty>&)__super::operator = (_Right);}

    smart_simple_ptr<_Ty>& operator = (_Ty* _Ptr)
    {return (smart_simple_ptr<_Ty>&)__super::operator = (_Ptr);}

private:
    template<class _Other> smart_simple_ptr<_Ty>                (const smart_ptr<_Ty, _Other>&);
    template<class _Other> smart_simple_ptr<_Ty>&    operator =    (const smart_ptr<_Ty, _Other>&);

    template<class _Other> smart_simple_ptr<_Ty>                (const smart_simple_ptr<_Other>&);
    template<class _Other> smart_simple_ptr<_Ty>&    operator =    (const smart_simple_ptr<_Other>&);
};

/************************************************************************/
/*           smart_gd_simple_ptr 單實體智能指針 (使用全局 delete)          */
/************************************************************************/

template<class _Ty>
class smart_gd_simple_ptr : public smart_ptr<_Ty, global_simple_deleter<_Ty>>
{
public:
    smart_gd_simple_ptr(_Ty* _Ptr = 0)                                        : smart_ptr(_Ptr)    {}
    smart_gd_simple_ptr(smart_gd_simple_ptr<_Ty>& _Right)                    : smart_ptr(_Right)    {}
    smart_gd_simple_ptr(smart_ptr<_Ty, global_simple_deleter<_Ty>>& _Right)    : smart_ptr(_Right)    {}

    smart_gd_simple_ptr<_Ty>& operator = (smart_ptr<_Ty, global_simple_deleter<_Ty>>& _Right)
    {return (smart_gd_simple_ptr<_Ty>&)__super::operator = (_Right);}

    smart_gd_simple_ptr<_Ty>& operator = (smart_gd_simple_ptr<_Ty>& _Right)
    {return (smart_gd_simple_ptr<_Ty>&)__super::operator = (_Right);}

    smart_gd_simple_ptr<_Ty>& operator = (_Ty* _Ptr)
    {return (smart_gd_simple_ptr<_Ty>&)__super::operator = (_Ptr);}

private:
    template<class _Other> smart_gd_simple_ptr<_Ty>                    (const smart_ptr<_Ty, _Other>&);
    template<class _Other> smart_gd_simple_ptr<_Ty>&    operator =    (const smart_ptr<_Ty, _Other>&);

    template<class _Other> smart_gd_simple_ptr<_Ty>                    (const smart_gd_simple_ptr<_Other>&);
    template<class _Other> smart_gd_simple_ptr<_Ty>&    operator =    (const smart_gd_simple_ptr<_Other>&);
};

/************************************************************************/
/*                   smart_array_ptr 數組智能指針                        */
/************************************************************************/

template<class _Ty>
class smart_array_ptr : public smart_ptr<_Ty, array_deleter<_Ty>>
{
public:
    smart_array_ptr(_Ty* _Ptr = 0)                                : smart_ptr(_Ptr)    {}
    smart_array_ptr(smart_simple_ptr<_Ty>& _Right)                : smart_ptr(_Right)    {}
    smart_array_ptr(smart_ptr<_Ty, array_deleter<_Ty>>& _Right)    : smart_ptr(_Right)    {}

    smart_array_ptr<_Ty>& operator = (smart_ptr<_Ty, array_deleter<_Ty>>& _Right)
    {return (smart_array_ptr<_Ty>&)__super::operator = (_Right);}

    smart_array_ptr<_Ty>& operator = (smart_array_ptr<_Ty>& _Right)
    {return (smart_array_ptr<_Ty>&)__super::operator = (_Right);}

    smart_array_ptr<_Ty>& operator = (_Ty* _Ptr)
    {return (smart_array_ptr<_Ty>&)__super::operator = (_Ptr);}

private:
    template<class _Other> smart_array_ptr<_Ty>                    (const smart_ptr<_Ty, _Other>&);
    template<class _Other> smart_array_ptr<_Ty>&    operator =    (const smart_ptr<_Ty, _Other>&);

    template<class _Other> smart_array_ptr<_Ty>                    (const smart_array_ptr<_Other>&);
    template<class _Other> smart_array_ptr<_Ty>&    operator =    (const smart_array_ptr<_Other>&);
};

/************************************************************************/
/*          smart_gd_array_ptr 數組智能指針 (使用全局 delete)          */
/************************************************************************/

template<class _Ty>
class smart_gd_array_ptr : public smart_ptr<_Ty, global_array_deleter<_Ty>>
{
public:
    smart_gd_array_ptr(_Ty* _Ptr = 0)                                        : smart_ptr(_Ptr)    {}
    smart_gd_array_ptr(smart_gd_array_ptr<_Ty>& _Right)                        : smart_ptr(_Right)    {}
    smart_gd_array_ptr(smart_ptr<_Ty, global_array_deleter<_Ty>>& _Right)    : smart_ptr(_Right)    {}

    smart_gd_array_ptr<_Ty>& operator = (smart_ptr<_Ty, global_array_deleter<_Ty>>& _Right)
    {return (smart_gd_array_ptr<_Ty>&)__super::operator = (_Right);}

    smart_gd_array_ptr<_Ty>& operator = (smart_gd_array_ptr<_Ty>& _Right)
    {return (smart_gd_array_ptr<_Ty>&)__super::operator = (_Right);}

    smart_gd_array_ptr<_Ty>& operator = (_Ty* _Ptr)
    {return (smart_gd_array_ptr<_Ty>&)__super::operator = (_Ptr);}

private:
    template<class _Other> smart_gd_array_ptr<_Ty>                (const smart_ptr<_Ty, _Other>&);
    template<class _Other> smart_gd_array_ptr<_Ty>&    operator =    (const smart_ptr<_Ty, _Other>&);

    template<class _Other> smart_gd_array_ptr<_Ty>                (const smart_gd_array_ptr<_Other>&);
    template<class _Other> smart_gd_array_ptr<_Ty>&    operator =    (const smart_gd_array_ptr<_Other>&);
};
 
________________________________________

 後記
•  對於內存管理,其實還有一種情形還沒講的,就是如何優雅地管理 vetor、list、map 這類容器中的指針,這個話題留到以後討論 STL 時再詳細闡述吧。
• 在本座的代碼中基本上看不到 free / delere 這類單詞(new 則是有的 —— 給智能指針賦值的時候 ^_^),就本座的經驗而言,封裝如果利用得當確實能減少很多麻煩,使代碼更清晰,有條理,降低錯誤發生幾率。
• 當然了,封裝並不是萬能,它不能解決所有問題,關鍵是靠個人的專注與細心。
• 本座碼字提出自己的觀點,旨在拋磚引玉,激發大家思考如何培養良好的編程習慣,不是權威,更不能盡信。最實在的知識應該來自個人最直接的體驗。
   敬請期待:《如何養成良好的 C++ 編程習慣(二)—— Windows 資源管理》

 

摘自 ~怪^_*獸~

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved