• 重构ATL中的CAutoVectorPtr, CAutoPtr和CAutoStackPtr


    看到ATL中有3个类的代码比较比较重复,在atlbase.h中,分别是CAutoVectorPtr, CAutoPtr和CAutoStackPtr,他们的功能其实很类似STL中的autoptr, 但是这里因为针对不同的分配对象而用了3个不同的类,其中CAutoVectorPtr是针对数组类型的,CAutoPtr是针对普通的非数组类型,而CAutoStackPtr针对的是_malloca分配的类型,因为最后释放方式的不同,它这里用了3份代码来实现。

    CAutoVectorPtr:
    template< typename T >
    class CAutoVectorPtr
    {
    public:
        CAutoVectorPtr() throw() :
            m_p( NULL )
        {
        }
        CAutoVectorPtr( CAutoVectorPtr< T >& p ) throw()
        {
            m_p = p.Detach();  // Transfer ownership
        }
        explicit CAutoVectorPtr( T* p ) throw() :
            m_p( p )
        {
        }
        ~CAutoVectorPtr() throw()
        {
            Free();
        }

        operator T*() const throw()
        {
            return( m_p );
        }

        CAutoVectorPtr< T >& operator=( CAutoVectorPtr< T >& p ) throw()
        {
            if(*this==p)
            {
                if(m_p == NULL)
                {
                    // This branch means both two pointers are NULL, do nothing.
                }
                else if(this!=&p)
                {
                    // If this assert fires, it means you attempted to assign one CAutoVectorPtr to another when they both contained 
                    
    // a pointer to the same underlying vector. This means a bug in your code, since your vector will get 
                    
    // double-deleted. 
                    ATLASSERT(FALSE);

                    // For safety, we are going to detach the other CAutoVectorPtr to avoid a double-free. Your code still
                    
    // has a bug, though.
                    p.Detach();
                }
                else
                {
                    // Alternatively, this branch means that you are assigning a CAutoVectorPtr to itself, which is
                    
    // pointless but permissible

                    
    // nothing to do
                }
            }
            else
            {
                Free();
                Attach( p.Detach() );  // Transfer ownership
            }
            return( *this );
        }

        // basic comparison operators
        bool operator!=(CAutoVectorPtr<T>& p) const
        {
            return !operator==(p);
        }

        bool operator==(CAutoVectorPtr<T>& p) const
        {
            return m_p==p.m_p;
        }

        // Allocate the vector
        bool Allocate( size_t nElements ) throw()
        {
            ATLASSUME( m_p == NULL );
            ATLTRY( m_p = new T[nElements] );
            if( m_p == NULL )
            {
                returnfalse );
            }

            returntrue );
        }
        // Attach to an existing pointer (takes ownership)
        void Attach( T* p ) throw()
        {
            ATLASSUME( m_p == NULL );
            m_p = p;
        }
        // Detach the pointer (releases ownership)
        T* Detach() throw()
        {
            T* p;

            p = m_p;
            m_p = NULL;

            return( p );
        }
        // Delete the vector pointed to, and set the pointer to NULL
        void Free() throw()
        {
            delete[] m_p;
            m_p = NULL;
        }

    public:
        T* m_p;
    };



    CAutoPtr:
    template< typename T >
    class CAutoPtr
    {
    public:
        CAutoPtr() throw() :
            m_p( NULL )
        {
        }
        template< typename TSrc >
        CAutoPtr( CAutoPtr< TSrc >& p ) throw()
        {
            m_p = p.Detach();  // Transfer ownership
        }
        CAutoPtr( CAutoPtr< T >& p ) throw()
        {
            m_p = p.Detach();  // Transfer ownership
        }
        explicit CAutoPtr( T* p ) throw() :
            m_p( p )
        {
        }
        ~CAutoPtr() throw()
        {
            Free();
        }

        // Templated version to allow pBase = pDerived
        template< typename TSrc >
        CAutoPtr< T >& operator=( CAutoPtr< TSrc >& p ) throw()
        {
            if(m_p==p.m_p)
            {
                // This means that two CAutoPtrs of two different types had the same m_p in them
                
    // which is never correct
                ATLASSERT(FALSE);
            }
            else
            {
                Free();
                Attach( p.Detach() );  // Transfer ownership
            }
            return( *this );
        }
        CAutoPtr< T >& operator=( CAutoPtr< T >& p ) throw()
        {
            if(*this==p)
            {
                if(this!=&p)
                {
                    // If this assert fires, it means you attempted to assign one CAutoPtr to another when they both contained 
                    
    // a pointer to the same underlying object. This means a bug in your code, since your object will get 
                    
    // double-deleted. 
    #ifdef ATL_AUTOPTR_ASSIGNMENT_ASSERT
                    ATLASSERT(FALSE);
    #endif

                    // For safety, we are going to detach the other CAutoPtr to avoid a double-free. Your code still
                    
    // has a bug, though.
                    p.Detach();
                }
                else
                {
                    // Alternatively, this branch means that you are assigning a CAutoPtr to itself, which is
                    
    // pointless but permissible

                    
    // nothing to do
                }
            }
            else
            {
                Free();
                Attach( p.Detach() );  // Transfer ownership
            }
            return( *this );
        }

        // basic comparison operators
        bool operator!=(CAutoPtr<T>& p) const
        {
            return !operator==(p);
        }

        bool operator==(CAutoPtr<T>& p) const
        {
            return m_p==p.m_p;
        }

        operator T*() const throw()
        {
            return( m_p );
        }
        T* operator->() const throw()
        {
            ATLASSUME( m_p != NULL );
            return( m_p );
        }

        // Attach to an existing pointer (takes ownership)
        void Attach( T* p ) throw()
        {
            ATLASSUME( m_p == NULL );
            m_p = p;
        }
        // Detach the pointer (releases ownership)
        T* Detach() throw()
        {
            T* p;

            p = m_p;
            m_p = NULL;

            return( p );
        }
        // Delete the object pointed to, and set the pointer to NULL
        void Free() throw()
        {
            delete m_p;
            m_p = NULL;
        }

    public:
        T* m_p;
    };

    CAutoStackPtr:
    /* Automatic cleanup for _malloca objects */
    template< typename T >
    class CAutoStackPtr
    {
    public:
        CAutoStackPtr() throw() :
            m_p( NULL )
        {
        }
        template< typename TSrc >
        CAutoStackPtr( CAutoStackPtr< TSrc >& p ) throw()
        {
            m_p = p.Detach();  // Transfer ownership
        }
        CAutoStackPtr( CAutoStackPtr< T >& p ) throw()
        {
            m_p = p.Detach();  // Transfer ownership
        }
        explicit CAutoStackPtr( T* p ) throw() :
            m_p( p )
        {
        }
        ~CAutoStackPtr() throw()
        {
            Free();
        }

        // Templated version to allow pBase = pDerived
        template< typename TSrc >
        CAutoStackPtr< T >& operator=( CAutoStackPtr< TSrc >& p ) throw()
        {
            if(m_p==p.m_p)
            {
                // This means that two CAutoPtrs of two different types had the same m_p in them
                
    // which is never correct
                ATLASSERT(FALSE);
            }
            else
            {
                Free();
                Attach( p.Detach() );  // Transfer ownership
            }
            return( *this );
        }
        CAutoStackPtr< T >& operator=( CAutoStackPtr< T >& p ) throw()
        {
            if(*this==p)
            {
                if(this!=&p)
                {
                    // If this assert fires, it means you attempted to assign one CAutoPtr to another when they both contained 
                    
    // a pointer to the same underlying object. This means a bug in your code, since your object will get 
                    
    // double-deleted. 
                    ATLASSERT(FALSE);

                    // For safety, we are going to detach the other CAutoPtr to avoid a double-free. Your code still
                    
    // has a bug, though.
                    p.Detach();
                }
                else
                {
                    // Alternatively, this branch means that you are assigning a CAutoPtr to itself, which is
                    
    // pointless but permissible

                    
    // nothing to do
                }
            }
            else
            {
                Free();
                Attach( p.Detach() );  // Transfer ownership
            }
            return( *this );
        }

        // basic comparison operators
        bool operator!=(CAutoStackPtr<T>& p) const
        {
            return !operator==(p);
        }

        bool operator==(CAutoStackPtr<T>& p) const
        {
            return m_p==p.m_p;
        }

        operator T*() const throw()
        {
            return( m_p );
        }
        T* operator->() const throw()
        {
            ATLASSUME( m_p != NULL );
            return( m_p );
        }

        // Attach to an existing pointer (takes ownership)
        void Attach( T* p ) throw()
        {
            ATLASSUME( m_p == NULL );
            m_p = p;
        }
        // Detach the pointer (releases ownership)
        T* Detach() throw()
        {
            T* p;

            p = m_p;
            m_p = NULL;

            return( p );
        }
        // Delete the object pointed to, and set the pointer to NULL
        void Free() throw()
        {
            /* Note: _freea only actually does anything if m_p was heap allocated
               If m_p was from the stack, it wouldn't be possible to actually free it here
               [wrong function] unless we got inlined. But really all we do if m_p is 
               stack-based is ignore it and let its alloca storage disappear at the end
               of the outer function.
            
    */
            _freea(m_p);
            m_p = NULL;
        }

    public:
        T* m_p;
    };

    可以看到上面代码明显非常重复,不知道ATL这样写是不是历史原因,我们下面尝试对它进行重构。

    可以看到其实他们只是最终释放(Free)的时候稍微有些差别,我们明显可以把写差别提取出来,作为一个释放的Policy。

    struct DeleteFunctor
    {
        template<typename T> static void Release(T* p) { delete p; }
    };

    struct DeleteArrayFunctor
    {
        template<typename T> static void Release(T* p) { delete []p; }
    };

    struct DeleteStackFunctor
    {
        template<typename T> static void Release(T* p) { _freea p; }
    };

    然后我们把上面的各种释放行为作为一个模板参数传进去就可以了,代码如下:
    template< typename T, typename ReleasePolicy>
    class CAutoReleasePtr
    {
    public:
        CAutoReleasePtr() throw() :
          m_p( NULL )
          {
          }
          template< typename TSrc >
          CAutoReleasePtr( CAutoReleasePtr< TSrc >& p ) throw()
          {
              m_p = p.Detach();  // Transfer ownership
          }
          CAutoReleasePtr( CAutoReleasePtr< T >& p ) throw()
          {
              m_p = p.Detach();  // Transfer ownership
          }
          explicit CAutoReleasePtr( T* p ) throw() :
          m_p( p )
          {
          }
          ~CAutoReleasePtr() throw()
          {
              Free();
          }

          // Templated version to allow pBase = pDerived
          template< typename TSrc >
          CAutoReleasePtr< T >& operator=( CAutoReleasePtr< TSrc >& p ) throw()
          {
              if(m_p==p.m_p)
              {
                  // This means that two CAutoPtrs of two different types had the same m_p in them
                  
    // which is never correct
                  ATLASSERT(FALSE);
              }
              else
              {
                  Free();
                  Attach( p.Detach() );  // Transfer ownership
              }
              return( *this );
          }
          CAutoReleasePtr< T >& operator=( CAutoReleasePtr< T >& p ) throw()
          {
              if(*this==p)
              {
                  if(this!=&p)
                  {
                      // If this assert fires, it means you attempted to assign one CAutoPtr to another when they both contained 
                      
    // a pointer to the same underlying object. This means a bug in your code, since your object will get 
                      
    // double-deleted. 
    #ifdef ATL_AUTOPTR_ASSIGNMENT_ASSERT
                      ATLASSERT(FALSE);
    #endif

                      // For safety, we are going to detach the other CAutoPtr to avoid a double-free. Your code still
                      
    // has a bug, though.
                      p.Detach();
                  }
                  else
                  {
                      // Alternatively, this branch means that you are assigning a CAutoPtr to itself, which is
                      
    // pointless but permissible

                      
    // nothing to do
                  }
              }
              else
              {
                  Free();
                  Attach( p.Detach() );  // Transfer ownership
              }
              return( *this );
          }

          // basic comparison operators
          bool operator!=(CAutoReleasePtr<T>& p) const
          {
              return !operator==(p);
          }

          bool operator==(CAutoReleasePtr<T>& p) const
          {
              return m_p==p.m_p;
          }

          operator T*() const throw()
          {
              return( m_p );
          }
          T* operator->() const throw()
          {
              ATLASSUME( m_p != NULL );
              return( m_p );
          }

          // Attach to an existing pointer (takes ownership)
          void Attach( T* p ) throw()
          {
              ATLASSUME( m_p == NULL );
              m_p = p;
          }
          // Detach the pointer (releases ownership)
          T* Detach() throw()
          {
              T* p;

              p = m_p;
              m_p = NULL;

              return( p );
          }
          // Delete the object pointed to, and set the pointer to NULL
          void Free() throw()
          {
              ReleasePolicy::Release(m_p);
              m_p = NULL;
          }

    public:
        T* m_p;
    };

    可以看到我们上面其实就改了一行代码,改了下最终的释放策略.

    好,现在我们可以这样用了:
    CAutoReleasePtr<T, DeleteFunctor> p1(new int);
    CAutoReleasePtr<T, DeleteArrayFunctor> p2(new int[10]);
    功能是可以了,但是是不是觉得上面这样用起来不方便,typedef一下就好了:
     typedef CAutoReleasePtr<T, DeleteFunctor> CSimplePtr<T>;
     typedef CAutoReleasePtr<T, DeleteArrayFunctor> CArrayPtr<T>;
     typedef CAutoReleasePtr<T, DeleteStackFunctor> CStackPtr<T>;
    但是我们很块发现上面的代码编译都过不了。

    既然typedef不行,那我们就通过继承来生成一个新类:
    template<typename T> class CSimplePtr: public CAutoReleasePtr<T, DeleteFunctor> {};
    template<typename T> class CArrayPtr: public CAutoReleasePtr<T, DeleteArrayFunctor> {};
    template<typename T> class CStackPtr: public CAutoReleasePtr<T, DeleteStackFunctor> {};
    我们很快又发现,用不起来,我们新类的构造函数需要重写才行。

    接下来我们考虑生成一个新类,然后在内部typedef:
     template<typename T>
     struct CSimplePtr
     {
         typedef CAutoReleasePtr<T, DeleteFunctor> type;
     };
     
     template<typename T>
     struct CArrayPtr
     {
         typedef CAutoReleasePtr<T, DeleteArrayFunctor> type;
     };
     
     template<typename T>
     struct CStackPtr
     {
         typedef CAutoReleasePtr<T, DeleteStackFunctor> type;
     };
    然后这样用:
    CSimplePtr<int>::type p(new int);
    CArrayPtr<int>::type p1(new int[20]);
    可是这样用和最初的用法似乎又没多少改进....

    再最后想到了用宏:
    #define CSimplePtr(T) CAutoReleasePtr<T, DeleteFunctor>
    #define CArrayPtr(T) CAutoReleasePtr<T, DeleteArrayFunctor>
    但是用的时候太呕心了:
    CSimplePtr(int) p(new int);
    CArrayPtr(int) p1(new int[20]);

    最后,实在没有什么办法了....
    不知道大家有没有什么好方法 ???
  • 相关阅读:
    Apache Ant 1.9.1 版发布
    Apache Subversion 1.8.0rc2 发布
    GNU Gatekeeper 3.3 发布,网关守护管理
    Jekyll 1.0 发布,Ruby 的静态网站生成器
    R语言 3.0.1 源码已经提交到 Github
    SymmetricDS 3.4.0 发布,数据同步和复制
    beego 0.6.0 版本发布,Go 应用框架
    Doxygen 1.8.4 发布,文档生成工具
    SunshineCRM 20130518发布,附带更新说明
    Semplice Linux 4 发布,轻量级发行版
  • 原文地址:https://www.cnblogs.com/weiym/p/2700815.html
Copyright © 2020-2023  润新知