我需要一个集合,我可以在其中存储具有虚函数的堆分配对象。
我知道boost::shared_ptr
,std::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 SB和Grizzly - 一般使用原始指针并建议使用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 - 类扩展一些基类(接口))调用并禁止重复指针。但是这个解决方案有克隆的开销。
答案 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)
,这几乎可以解决多个实例的问题(只要指针始终被包裹)。