2010-11-15 59 views
6

每当我需要动态分配的对象添加到载体中我一直在做下列方式:将vector_back'动态分配的对象'push到vector是否安全?

class Foo { ... }; 

vector<Foo*> v; 

v.push_back(new Foo); 

// do stuff with Foo in v 

// delete all Foo in v 

这只是工作和许多其他人似乎做同样的事情。

今天,我了解到vector :: push_back可以抛出异常。这意味着上面的代码不是特例安全的。 :-(所以我想出了一个解决方案:

class Foo { ... }; 

vector<Foo*> v; 
auto_ptr<Foo> p(new Foo); 

v.push_back(p.get()); 
p.release(); 

// do stuff with Foo in v 

// delete all Foo in v 

但问题是,新的方法是冗长,乏味的,我看没有人这样做(至少不在我身边......)

我应该去的新途径?
或者说,我能不能用老办法坚持?
或者,有没有这样做的更好的办法?

+0

另请参阅:为什么是错误的使用std :: auto_ptr的<>与STL容器?] [1] [1]:http://stackoverflow.com/questions/111478/why-is -it-wrong-to-use-stdauto-ptr-with-stl-containers – 2010-11-15 14:52:11

+1

@Martin:实际上在这里使用'auto_ptr'和'vector'没有问题(我的意思是,特定于它们的交互),因为它不是我们正在谈论的'vector >“。 – 2010-11-15 15:01:34

+0

@Maththieu:你说得对。然后我再次避免*说*有问题,虽然我的链接可能暗示它:-) – 2010-11-15 15:44:41

回答

11

如果你所关心的是这种操作的异常安全:

v.reserve(v.size()+1); // reserve can throw, but that doesn't matter 
v.push_back(new Foo); // new can throw, that doesn't matter either. 

具有释放的对象由它的内容所指向的是一个独立的东西的责任向量的问题,我相信你会得到大量的建议;-)

编辑:嗯,我要引用标准,但我实际上找不到必要的保证。我要找的是push_back不会抛出,除非(a)必须重新分配(我们知道它不会因为容量),或者(b)T的构造函数抛出(我们知道它已经赢了因为T是指针类型)。听起来很合理,但合理!=保证。

所以,除非有一个有益的答案比对这个问题:

Is std::vector::push_back permitted to throw for any reason other than failed reallocation or construction?

此代码依赖于没有做任何事情太“有想象力”的执行情况。如果做不到这一点,从问题的解决方案可以作为模板起来:然后

template <typename T, typename Container> 
void push_back_new(Container &c) { 
    auto_ptr<T> p(new T); 
    c.push_back(p.get()); 
    p.release(); 
} 

用法是不是太乏味:

struct Bar : Foo { }; 

vector<Foo*> v; 
push_back_new<Foo>(v); 
push_back_new<Bar>(v); 

如果这真是一个工厂函数,而不是new,那么你可以修改模板因此。不过,在不同的情况下传递大量不同的参数列表将会很困难。

+0

是的,对于这个问题,我关心的只是异常安全。谢谢! – upriser 2010-11-15 15:16:53

0

如何抵御内存不足是你的计划吗?如果你真的关心这个,你必须为new做好准备。如果你我不会去处理这个问题,我不会担心跳过篮筐。

在一般情况下,如果内存不足,程序已经有可能不可逾越的问题,除非它专门设计为永久运行在受限制的覆盖区域(嵌入式系统) - 在这种情况下,您必须关心所有这些情况。

如果这适用于您,您可以在您面前进行冗长的代码审查和重新测试。尽管如此,我的猜测是你可以在这里遵循你的团队的实践。

正如其他人指出的那样,使用vector来存储原始指针有其自身的问题,并且本网站上还有大量资料以及将您引导到更安全模式的其他答案。

+0

不回答OP的查询 – Chubsdad 2010-11-15 14:45:24

+0

@Chubsdad - 问题被标记为'异常安全',所以我认为这是相关的。建议的修补程序都会讲到'vector'中的原始指针使用情况,而不处理'new'抛出时会发生什么情况。 – 2010-11-15 14:49:49

+1

如果'新Foo'抛出,没有分配内存。如果'vector'重新分配抛出,我们只是丢失了'new Foo'分配的内存地址。如果内存中的内容太短或内存过于分散,则矢量重新分配可能会失败,而不会在内存中“短暂”。 – 2010-11-15 15:03:51

11

你的新方法是更多异常安全,但有一个原因,你没有看到它在其他地方完成。

指针的A vector只拥有指针,它不表示指向对象的所有权。您正在有效地向不“拥有”所有权的对象释放所有权。

大多数人会使用vectorshared_ptr来正确表达所有权或使用类似boost::ptr_vector之类的东西。这两者中的任何一个意味着您不必明确指定要存储指针的对象,这些对象容易出错,并且可能在程序中的其他位置出现“危险”异常。

编辑:插入ptr_vector后仍需非常小心。不幸的是,push_back采取一个原始指针提供了强有力的保证,这意味着插入成功或(有效)没有发生,所以传入的对象既不被接管也不被销毁。在调用强保证版本之前,按值定义智能指针的版本被定义为调用.release(),这实际上意味着它可能会泄漏。

使用vectorshared_ptr连同make_shared更容易正确使用。

+1

“在调用强保证版本之前定义为调用.release()” - 这是一种愤怒。不是吗? – 2010-11-15 15:01:52

+1

@Steve:同意。一旦所有权成功转移后,通过非''const'引用获取'auto_ptr'并且只有'release'会更有意义。 – 2010-11-15 15:04:29

+0

听起来不错,和'shared_ptr(auto_ptr)'构造函数一样。对于这个用法,我还会解决它的值,并释放或销毁,相当于'shared_ptr(T *)'构造函数,但应用于auto_ptr参数的指针内容。 – 2010-11-15 15:08:39

3

做到这一点的优选方法是使用智能指针的容器,例如,一个std::vector<std::shared_ptr<Foo> >std::vector<std::unique_ptr<Foo> >shared_ptr可以在升压和C++ TR1中找到,以及; std::unique_ptr被有效地限制在C++ 0X) 。

另一种选择是使用拥有动态对象的容器,例如由Boost指针容器库提供的​​容器。

相关问题