带引用计数的C ++指针容器

时间:2012-01-08 01:52:00

标签: c++ boost stl

我需要一个集合,我可以在其中存储具有虚函数的堆分配对象。

我知道boost::shared_ptrstd::unique_ptr(C ++ 11)和boost::ptr_(vector|list|map),但它们无法解决重复指针问题。

只是为了描述一个问题 - 我有一个接受堆分配指针并将其存储以备将来使用的函数:

void SomeClass::add(T* ptr)
{
  _list.push_back(ptr);
}

但是,如果我使用相同的参数add两次调用ptr - _list将包含指向同一对象的两个指针,并且当_list被破坏时,将发生多个相同对象的删除

如果_list计算他存储的指针并在删除时使用它们,那么这个问题就会解决,对象不会被多次删除。

所以问题是:

有人知道一些带有指针的集合(矢量,列表,映射本质上)的指针,在销毁时自动删除并支持引用计数吗?

或许我可以使用其他技术解决这个问题?

更新

我需要支持重复指针。所以我不能使用std::set

提到Kerrek SBGrizzly - 一般使用原始指针并建议使用std::make_shared并忘记通过new进行实例化是一个坏主意。但这是客户端代码的责任 - 而不是我设计的类。即使我将add签名(当然还有_list容器)更改为

void SomeClass::add(std::shared_ptr<T> ptr)
{
   _list.push_back(ptr);
}

然后有人(不知道std::make_shared)仍然可以写下这个:

SomeClass instance;
T* ptr = new T();
instance.add(ptr);
instance.add(ptr);

所以这不是我等待的完整解决方案,但如果你单独编写代码就很有用。

更新2:

作为替代解决方案,我发现了一个克隆(使用生成的拷贝构造函数)。我的意思是我可以改变我的add函数:

template <typename R>
void SomeClass::add(const R& ref)
{
  _list.push_back(new R(ref));
}

这将允许虚方法(R - 类扩展一些基类(接口))调用并禁止重复指针。但是这个解决方案有克隆的开销。

2 个答案:

答案 0 :(得分:1)

是:std::list<std::shared_ptr<T>>

共享指针可以来自<memory>,也可以来自<tr1/memory>或Boost <boost/shared_ptr.hpp>的旧版平台。您不需要手动删除任何,因为共享指针会自行处理。但是,您需要从一开始就将所有堆指针保留在共享指针内:

std::shared_ptr<T> p(new T);    // legacy
auto p = std::make_shared<T>(); // better

如果您是另一个共享指向同一对象的指针,请复制共享指针(而不是从基础原始指针构造新的共享指针):auto q = p;


这里的道德是:如果你使用裸指针,那就错了。

答案 1 :(得分:1)

通过比较底层容器来实现智能指针的比较。所以你可以使用你喜欢的任何智能指针的std::set。我个人使用std::unique_ptr而不是shared_ptr,每当我可以逃脱它时,因为它使所有权更加清晰(持有unique_ptr的人是所有者)并且开销也低得多。我发现这几乎适用于我的所有代码。代码看起来如下所示:

std::set<std::unique_ptr<T> > _list;

void SomeClass::add(T* ptr)
{
   std::unique_ptr<T> p(ptr);
   auto iter = _list.find(p);
   if(iter == _list.end())
     _list.insert(std::move(p));
   else
     p.release();
}

我现在不确定是否过度杀伤(如果插入失败,必须检查insert是否保证不做任何事情),但它应该有效。由于缺少shared_ptr<T>成员,使用relase执行此操作看起来会相似,但有点复杂。在这种情况下,我可能会首先构建一个shared_ptr<T>,其中无效deleter也会传递给查找调用,然后是另一个实际插入的shared_ptr<T>

当然,我个人会避免这样做,并且当指针的所有权转手时总是传递智能指针。因此,我会将SomeClass::add重写为void SomeClass::add(std::unique_ptr<T> ptr)void SomeClass::add(std::shared_ptr<T> ptr),这几乎可以解决多个实例的问题(只要指针始终被包裹)。