在C ++中使用“this”的智能指针

时间:2009-02-26 22:23:15

标签: c++ this smart-pointers

我一直在努力用引用计数指针替换原始指针,这些指针只暴露底层的const版本。我的目标是减少内存使用(以及不必要地构建和破坏复杂对象所花费的时间),而不是让自己处于任何代码都可以访问它不拥有的内存的情况。我知道引用计数的循环引用问题,但我的代码永远不应该创建这样的情况。

要求const-ness有效,因为我使用的是一个系统,其中类通常不暴露任何非const成员,而不是更改对象,您必须在其上调用一个返回由更改产生的新对象的方法。这可能是一种设计模式,但我不知道它的名字。

当我有一个返回指向其类型对象的指针的方法时,我的问题就出现了,有时它本身也是如此。以前它看起来像这样:

Foo * Foo::GetAfterModification( const Modification & mod ) const
{
  if( ChangesAnything( mod ) )
  {
    Foo * asdf = new Foo;
    asdf.DoModification( mod );
    return asdf;
  }
  else
    return this;
}

我看不出让这个返回智能指针的好方法。天真的方法类似于return CRcPtr< Foo >( this ),但这打破了所有权语义,因为我正在返回以及之前拥有该对象的人,每个人都认为他们拥有所有权但彼此不了解。唯一安全的做法是return CRcPtr< Foo >( new Foo( *this ) ),但这无法限制不必要的内存使用。

有没有办法安全地返回智能指针而不分配任何额外的内存?我怀疑是没有。如果有,如果对象已在堆栈上分配,它将如何工作? This question似乎是相关的,但不一样,因为他可以让他的函数将原始指针作为参数,因为他正在使用boost库。

作为参考,我的自制智能指针实现如下。我相信它可能更强大,但它比依赖boost或tr1在任何地方都可用更便携,并且直到这个问题它对我来说都很好。

template <class T>
class CRcPtr
{
public:
  explicit CRcPtr( T * p_pBaldPtr )
  {
    m_pInternal = p_pBaldPtr;
    m_iCount = new unsigned short( 1 );
  }

  CRcPtr( const CRcPtr & p_Other )
  { Acquire( p_Other ); }

  template <class U>
  explicit CRcPtr( const CRcPtr< U > & p_It )
  {
    m_pInternal = dynamic_cast< T * >( p_It.m_pInternal );
    if( m_pInternal )
    {
      m_iCount = p_It.m_iCount;
      (*m_iCount)++;
    }
    else
      m_iCount = new unsigned short( 1 );
  }

  ~CRcPtr()
  { Release(); }

  CRcPtr & operator=( const CRcPtr & p_Other )
  {
    Release();
    Acquire( p_Other );
  }

  const T & operator*() const
  { return *m_pInternal; }

  const T * operator->() const
  { return m_pInternal; }

  const T * get() const
  { return m_pInternal; }

private:
  void Release()
  {
    (*m_iCount)--;
    if( *m_iCount == 0 )
    {
      delete m_pInternal;
      delete m_iCount;
      m_pInternal = 0;
      m_iCount = 0;
    }
  }

  void Acquire( const CRcPtr & p_Other )
  {
    m_pInternal = p_Other.m_pInternal;
    m_iCount = p_Other.m_iCount;
    (*m_iCount)++;
  }

  template <class U>
  friend class CRcPtr;

  T * m_pInternal;
  unsigned short * m_iCount;
};

template <class U, class T>
CRcPtr< U > ref_cast( const CRcPtr< T > & p_It )
{ return CRcPtr< U >( p_It ); }

编辑:感谢您的回复。我希望避免使用boost或tr1,但我认识到不使用经过良好测试的库通常是不明智的。我很确定我所实现的是类似于std :: auto_ptr,而是类似于tr1 :: shared_ptr,除了它只暴露内部指针的const版本并且缺少一些正式版的功能。我真的想避免像Gian Paolo提出的那样的侵入式计划。我知道我的实现不是线程安全的,但这是一个单线程应用程序。

3 个答案:

答案 0 :(得分:5)

看一下Boost shared pointers的来源。如果从enable_shared_from_this<T>派生一个类,则可以调用shared_from_this()成员函数来执行此类操作。

答案 1 :(得分:0)

就语义而言,关于不变性的好处是你可以安全地在对象,线程之间共享数据,而不必担心人们会改变你所依赖的值。只要您的引用计划是正确的(我没有读过您的,但无论如何都使用Boost),您不必担心所有权。

答案 2 :(得分:0)

如Ferruccio所暗示,您需要某种形式的共享指针。即,可以在多个对象之间共享(安全!)。使这项工作的一种方法是从实现实际引用计数的类派生所有对象(至少是那些旨在与共享指针一起使用的对象)。这样,实际指针本身随身携带当前计数,并且您可以根据需要在多个不同对象之间共享指针。

您目前所拥有的与std :: auto_ptr非常相似,并且您遇到了与所有权相同的问题。谷歌,你应该找到一些有用的信息。

请注意,共享指针存在其他并发症:特别是在多线程环境中,引用计数必须是原子的,并且必须处理自我赋值以避免增加内部计数,以便永远不会破坏对象。

谷歌再次成为你的朋友。只需查看有关C ++共享指针的信息,您就会发现大量信息。