Develop good C++ programming habits within the management of the application

  • 2020-04-01 23:28:26
  • OfStack

The opening takeaway      

Although this series of articles is designed as popular science books, we believe that they are not only suitable for beginners to learn from, but also can trigger the reflection and resonance of the old birds. I welcome your valuable comments and feedback

Before we begin with the main content of this chapter, let me begin with a short discussion of a good working habit -- accumulation, refinement and refinement. In the process of work and study, the knowledge acquired is constantly accumulated in an effective way to form its own knowledge base. With the expansion of knowledge, it will be improved from quantitative change to qualitative change. In addition, it is necessary to continuously refine the knowledge. With the expansion of the scope of knowledge and the improvement of the level, you will surely find that the original knowledge base is one-sided, limited, clumsy and even wrong. At this time, you need to have the attitude of excellence and perseverance to optimize the knowledge base.

Maybe all of you have thought about doing it in the past, and you know why, but you just give yourself all kinds of excuses not to take the time to do it. To put it this way, the technology road is not easy. The industry has two basic requirements: 1. Endure loneliness. Otherwise, it is better to change careers early when you are young, or to do sales, products or management of the software industry, in short, do not do development

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

Memory management related issues

When it comes to memory management in C/C++, most people start thinking new/delete/malloc/free. Indeed, manual memory management in C/C++ is one of the things that sets them apart from other languages and ACTS as a barrier for those who want to switch from other languages to C/C++. This also caused the big forums to "C++ popularity is low" and "whether should introduce garbage collection mechanism" and other related topics fierce debate. This seat has always ignored these arguments. It is not that this seat does not care about the development and fate of C++. On the contrary, this seat is very concerned. Although from the current perspective, no matter how many hard wounds on C++, how the C++ committee and C++ compiler manufacturers of the masters of how to pull the cat tail. After all, love is love, incomplete beauty is beautiful, do not explain. The reason why we don't care about these arguments is that we have seen through that a language is just like a kind of life, which has a life cycle. The decline is only a matter of speed, and the old will always be replaced by the new, which is an objective law and inevitable. Didn't qin shihuang finally find the elixir of immortality? As long as the light has been hot, while still valuable for the public use is no regrets. On the contrary, we are very interested in the birth of new languages. We will learn about their characteristics and see what problems they can help solve. As in the past few years, Java and some dynamic languages (which do solve a lot of problems) have been most used due to work requirements, while C/C++ is no longer used.

Well, anyway, let's get back to business. Speaking of C/C++ Memory management seems daunting, full screen new/delete/malloc/free, OutPut window endless Memory Leak warning, the program weird 0X00000004 pointer exception, as if back to that year we cried together days, you Hold it? In fact, the reality is not as bad as you think. Just a little bit of effort, a little bit of thought, yes! Just a little - encapsulating memory access with C++ classes will solve most of your problems and benefit you for life. Take Windows program as an example, there are mainly the following memory management methods:

The & # 8226; Virtual memory (Virtual Memory)
The & # 8226; Default heap and private heap (Process Heap & Private Heap)
The & # 8226; Memory-mapped file (the File Mapping)
The & # 8226; Process stack (Heap, which is simply to cut off pieces in the Process Heap using malloc() or the default new operator.)
The & # 8226; The stack (Stack, memory is automatically managed by the caller or the called)
Today our topic is encapsulation, and we won't cover the concepts of each memory model and how the API is used. The first four types of memory access are encapsulated in C++ in much the same way, by allocating memory in a constructor or other manipulation function and then ensuring that memory is properly freed in a destructor. Virtual memory, default Heap, and Private Heap operate in similar ways, which is not shown here. Interested friends can refer to the article published a few days ago that nobody asked: "C++ encapsulates Private Heap", alas! The following encapsulation of memory-mapped files is only briefly introduced. We will focus on the encapsulation of the most frequently used malloc() and new.

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

  Memory-mapped file

The following code encapsulates the FileMapping handle and the memory mapped from the FileMapping into CFileMapping and CShareMemory, respectively. You can use CShareMemory directly to create a memory for FileMapping and FileMapping.


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)
};

 

Careful friends will realize that there are drawbacks to this encapsulation: first, CShareMemory can only share memory, not map to a real file (hFile is always INVALID_HANDLE_VALUE); Second, you can further encapsulate CShareMemory's Alloc() and Free() methods by automatically calling Free() with the destructor of the encapsulation class so that "set" can be completely eliminated < ULONG_PTR > M_set "; Third, CFileMapping can also encapsulate the file handles so that everything from CreateFile() to CreateFileMapping() is controlled. This imperfect encapsulation should be used as a negative example

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

Malloc () series of functions

Many people recommend that you replace malloc() with the new operator in C++ whenever possible, because of new type safety, automatic constructor and destructor calls, and so on. There are some situations where malloc() is actually better than new, and we can get away with it in terms of efficiency (almost all compiler new operators use malloc() to allocate memory), as those of you who have been working on low-level development know, we can't avoid dealing with row data (e.g. : Socket send buffer, etc.) data, this data is very suitable for use malloc (), use new memory allocated more pauses to think about what is the use of the delete, delete [], : : delete, : : delete [] which of the release, malloc () the memory allocated to all need not want to, free () fit all, but a realloc () can be easily to adjust the memory, do you have any "renew"? In a word, malloc() is really necessary, let's see how we can encapsulate it.


//T  & have spent & have spent & have spent & have spent & have spent & have spent & have spent & have spent & have spent & have spent & have spent & have spent & have spent & have spent & have spent : data type (built-in type or structure)
//MAX_CACHE_SIZE & have spent & have spent & have spent : the maximum number of preapplied memory, in units of sizeof(T), if the value is set reasonably, for
//& have spent & have spent & have spent & have spent & have spent & have spent & have spent & have spent & have spent & have spent & have spent & have spent & have spent & have spent & have spent & have spent & have spent & have spent & have spent & have spent A buffer that needs to dynamically increment the buffer can greatly improve efficiency
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);}
    //There are two ways to copy a constructor
    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;
    }
    //Dynamic buffer expansion
    template<size_t S> CBufferPtrT& Cat(const CBufferPtrT<T, S>& other)
    {
        if((void*)&other != (void*)this)
            Cat(other.Ptr(), other.Size());
        return *this;
    }
    //Dynamic buffer expansion
    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;}
    //Aha, it turns out to be type safe
    operator    T*    ()    {return Ptr();}
    operator const    T*    ()    const    {return Ptr();}
    //Wow, index access is supported
    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);}
    //There are two scenarios for the assignment operator
    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;
};
//Type defs of type buffer are commonly used
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

Well. To explain why you need two copy constructors and assignment operator overloads, first, the compiler generates different classes for different template parameters, that is, CBufferPtrT < Int, 1 > And CBufferPtrT < Int, 2 > In addition, the C++ compiler provides a default implementation (shallow copy) for each class that provides copy constructor and assignment operator overloading. Therefore, the first set of copy constructors and assignment operator overloading described above is the default implementation of the overwriting compiler, and the second set of copy constructors and assignment operator overloading handles the conversion of other classes to this class.

This block is happy with the package (the only fly in the face is that the editor of cnblogs is too corrupt to mess up the code), and it's not just a normal malloc() package, but rather a "type-safe dynamic buffer that supports index access." If it is placed in a socket communication class as a member property, it is best to act as a receive buffer and a send buffer accessed across multiple threads and methods (doing the synchronization yourself, of course). You can debug the following test example to see how it works:


 The test case  
 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;

     //The failure of the following line to compile indicates that the class is type-safe
 // bufferI = bufferS;

     return 0;
 }


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

New & delete

When it comes to the encapsulation of new, you immediately think of the smart pointer! Yes, smart Pointers. However, the auto_ptr provided by STL has many defects, and it is not convenient to use in the first place. Even this writing method is not supported: "STD: : auto_ptr < int >PI = new int." God's reason! What's more hateful is that array Pointers are not supported (delete[] is required), and there are many problems with using it if some classes overload the new operator, and many other disadvantages (I forgot to ^_^). However, C++0x seems to have made a major improvement on smart Pointers, there are already smart Pointers that support reference counting, but I don't know if it solves the problems of array Pointers and the difference between delete and ::delete. In any case, the smart pointer listed below supports the distinction between delete/delete[] / ::delete / ::delete[]. As an improvement on auto_ptr (no reference counting either), the article is too long and the test cases are not issued. Please try it yourself:





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;
};




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>&);
};



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>&);
};



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>&);
};



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>&);
};

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

  Afterword.

The & # 8226; One of the things that hasn't been covered in memory management is how to gracefully manage Pointers in containers like vetor, list, and map, which I'll save for a later discussion of the STL.
The & # 8226; There are few free/delere words in this block's code (there is new - when assigning a value to a smart pointer). In this block's experience, encapsulation, if used properly, can really save a lot of trouble, make the code clearer and more organized, and reduce the chance of errors.
The & # 8226; Of course, encapsulation is not a panacea, it can't solve all problems, the key is to rely on personal focus and care.
The & # 8226; This block code word puts forward own viewpoint, aim to throw off brick to lead jade, stimulate everybody to think how to cultivate good programming habit, not authority, still can't completely believe. The most solid knowledge should come from the most direct experience of the individual.


Related articles: