养成良好的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 通信类中作为成员属性,充当跨越多个线程和多个方法访问的接收缓冲区和发送缓冲区的角色就最适合不过了(当然要自己做同步了)。大家可以调试一下下面的测试例子,了解一下它的用法:


代码如下:

测试用例
 int _tmain(int argc, _TCHAR* argv[])
 {
     CBufferPtr buffer;

unsigned char c1    = 'X';
     unsigned char pc1[] = "123";
     unsigned char pc2[] = "abc";
     buffer.Cat(&c1);
     buffer.Cat(pc1, 3);
     buffer.Cat(pc2, 3);

CBufferPtrT<unsigned char, 10> buffer2 = buffer;
     buffer2.Cat(buffer);
     buffer2.Realloc(0);

unsigned char* pc = buffer;
     const unsigned char& c = buffer[5];
     buffer[5] = 'O';

short i1    = 0x7FFF;
     short pi0[] = {9,9,9};
     short pi1[] = {1,2,3};
     short pi2[] = {4,5,6};
     short pi3[] = {8,8,8};

CBufferPtrT<short, 10> bufferS(pi0, 3);

bufferS.Cat(&i1);
     bufferS.Cat(pi1, 3);
     bufferS.Cat(pi2, 3);
     bufferS.Cat(pi3, 3);

CBufferPtrT<short, 5> bufferS2;
     bufferS2.Malloc(4);

bufferS2 = bufferS;
     bufferS2.Realloc(30);

CBufferPtrT<int> bufferI(5, true);

for(size_t i = 0; i < bufferI.Size(); i++)
         bufferI[i] = i *10;

bufferI.Malloc();
     bufferI[0] = 123;

// 下面这行编译不通过,正好说明这个类是类型安全的
 // bufferI = bufferS;

return 0;
 }

--------------------------------------------------------------------------------

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 则是有的 —— 给智能指针赋值的时候 ^_^),就本座的经验而言,封装如果利用得当确实能减少很多麻烦,使代码更清晰,有条理,降低错误发生几率。
•当然了,封装并不是万能,它不能解决所有问题,关键是靠个人的专注与细心。
•本座码字提出自己的观点,旨在抛砖引玉,激发大家思考如何培养良好的编程习惯,不是权威,更不能尽信。最实在的知识应该来自个人最直接的体验。

(0)

相关推荐

  • 老生常谈C/C++内存管理

    内存分配方式 简介 在C++中,内存分成5个区,他们分别是堆.栈.自由存储区.全局/静态存储区和常量存储区. 栈:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放.栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限. 堆:就是那些由 new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个 delete.如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收. 自由存储区:就是那些由mall

  • 养成良好的C++编程习惯之内存管理的应用详解

    开篇导读    虽然本系列文章定位为科普读物,但本座相信它们不但适合新手们学习借鉴,同时也能引发老鸟们的反思与共鸣.欢迎大家提出宝贵的意见和反馈 ^_^ 在开篇讲述本章主要内容之前,本座首先用小小篇幅论述一下一种良好的工作习惯 -- 积累.提炼与求精.在工作和学习的过程中,不断把学到的知识通过有效的方式积累起来,形成自己的知识库,随着知识量的扩大,就会得到从量变到质变的提升.另外还要不断地对知识进行提炼,随着自己知识面的扩大以及水平的提升,你肯定会发现原有知识库存在着一些片面.局限.笨拙甚至错误

  • C语言编程数据在内存中的存储详解

    目录 变量在计算机中有三种表示方式,原码反码,补码 原码 反码 补码 总结一下 浮点数在内存的储存 C语言中,有几种基本内置类型. int unsigned int signed int char unsigned char signed char long unsigned long signed long float double 在内存中创建变量,会在内存中开辟空间,并为其赋值. int a=10; 在计算机中,所有数据都是以二进制的形式存储在内存中. 变量在计算机中有三种表示方式,原码反

  • Golang 内存管理简单技巧详解

    目录 引言 预先分配切片 结构中的顺序字段 使用 map[string]struct{} 而不是 map[string]bool 引言 除非您正在对服务进行原型设计,否则您可能会关心应用程序的内存使用情况.内存占用更小,基础设施成本降低,扩展变得更容易/延迟. 尽管 Go 以不消耗大量内存而闻名,但仍有一些方法可以进一步减少消耗.其中一些需要大量重构,但很多都很容易做到. 预先分配切片 数组是具有连续内存的相同类型的集合.数组类型定义指定长度和元素类型.数组的主要问题是它们的大小是固定的——它们

  • python内存管理机制原理详解

    python内存管理机制: 引用计数 垃圾回收 内存池 1. 引用计数 当一个python对象被引用时 其引用计数增加 1 ; 当其不再被变量引用时 引用计数减 1 ; 当对象引用计数等于 0 时, 对象被删除(引用计数是一种非常高效的内存管理机制) 2. 垃圾回收 垃圾回收机制: ① 引用计数 , ②标记清除 , ③分带回收 引用计数 : 引用计数也是一种垃圾收集机制, 而且也是一种最直观, 最简单的垃圾收集技术.当python某个对象的引用计数降为 0 时, 说明没有任何引用指向该对象, 该

  • java并发编程_线程池的使用方法(详解)

    一.任务和执行策略之间的隐性耦合 Executor可以将任务的提交和任务的执行策略解耦 只有任务是同类型的且执行时间差别不大,才能发挥最大性能,否则,如将一些耗时长的任务和耗时短的任务放在一个线程池,除非线程池很大,否则会造成死锁等问题 1.线程饥饿死锁 类似于:将两个任务提交给一个单线程池,且两个任务之间相互依赖,一个任务等待另一个任务,则会发生死锁:表现为池不够 定义:某个任务必须等待池中其他任务的运行结果,有可能发生饥饿死锁 2.线程池大小 注意:线程池的大小还受其他的限制,如其他资源池:

  • 关于Java变量的声明、内存分配及初始化详解

    实例如下: class Person { String name; int age; void talk() { System.out.println("我是: "+name+", 今年: "+age+"岁"); } } public class TestJava2_1 { public static void main(String args[]) { Person p; if (p == null) { p = new Person(); }

  • Linux共享内存实现机制的详解

    Linux共享内存实现机制的详解 内存共享: 两个不同进程A.B共享内存的意思是,同一块物理内存被映射到进程A.B各自的进程地址空间.进程A可以即时看到进程B对共享内存中数据的更新,反之亦然.由于多个进程共享同一块内存区域,必然需要某种同步机制,互斥锁和信号量都可以. 效率: 采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝.对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据[1]: 一次从输入文件到

  • Java编程Post数据请求和接收代码详解

    这两天在做http服务端请求操作,客户端post数据到服务端后,服务端通过request.getParameter()进行请求,无法读取到数据,搜索了一下发现是因为设置为text/plain模式才导致读取不到数据 urlConn.setRequestProperty("Content-Type","text/plain; charset=utf-8"); 若设置为以下方式,则通过request.getParameter()可以读取到数据 urlConn.setReq

  • PHP内存溢出优化代码详解

    相信很多人做大批量数据导出和数据导入的时候,经常会遇到PHP内存溢出的问题,在解决了问题之后,总结了一些经验,整理成文章记录下. 优化点 1.优化SQL语句,避免慢查询,合理的建立索引,查询指定的字段,sql优化这块在此就不展开了. 2.查询的结果集为大对象时转数组处理,框架中一般有方法可以转,如Laravel中有toArray(),Yii2中有asArray(). 3.对于大数组进行数据切割处理,PHP函数有array_chunk().array_slice(). 4.对于大型的字符串和对象,

  • Java基础之内存泄漏与溢出详解

    一.浅析 内存泄露( memory leak):是指程序在申请内存后,无法释放已申请的内存空间,多次内存泄露堆积后果很严重,内存迟早会被占光.内存泄漏最终会造成内存溢出. 内存溢出(out of memory) :是指程序在申请内存时,没有足够的内存空间供其使用 JVM中有一下几种内存空间: 栈内存(Stack):每个线程私有的. 堆内存(Heap):所有线程公用的. 方法区(Method Area):有点像以前常说的"进程代码段",这里面存放了每个加载类的反射信息.类函数的代码.编译

随机推荐