2

我一直致力于用引用计数指针替换原始指针,这些指针仅公开底层指针的 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 ) ),但这违背了我限制不必要的内存使用的意图。

有没有办法在不分配任何额外内存的情况下安全地返回智能指针?我的怀疑是没有。如果有,如果对象已在堆栈上分配,它将如何工作? 这个问题似乎相关,但并不相同,因为他可以让他的函数将原始指针作为参数,并且因为他使用的是 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 提出的那种侵入式方案。我知道我的实现不是线程安全的,但这是一个单线程应用程序。

4

3 回答 3

5

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

于 2009-02-26T22:31:45.437 回答
0

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

于 2009-02-26T23:41:47.483 回答
0

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

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

请注意,共享指针还有其他复杂性:特别是在多线程环境中,您的引用计数必须是原子的,并且必须处理自分配以避免增加内部计数,从而永远不会破坏对象。

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

于 2009-02-26T23:49:06.063 回答