将'动态分配的对象'推送到向量是否安全?

时间:2010-11-15 14:37:47

标签: c++ vector exception-safe

每当我需要将动态分配的对象添加到向量中时,我一直按照以下方式执行:

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

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

我应该采用新的方式吗?
或者,我可以坚持旧方式吗? 或者,有更好的方法吗?

4 个答案:

答案 0 :(得分:11)

您的新方式是更多例外安全,但您有理由不在其他地方看到它。

vector指针只拥有指针,它不表示指向对象的所有权。您实际上是将所有权释放给不想“拥有”所有权的对象。

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

修改:您仍需要非常小心地插入ptr_vector。不幸的是,push_back采用原始指针提供了强有力的保证,这意味着插入成功或(有效地)没有发生,因此传入的对象既不被接管也不被破坏。采用智能指针的版本定义为在调用强保证版本之前调用.release(),这实际上意味着它可能泄漏。

使用vector shared_ptrmake_shared更容易正确使用。

答案 1 :(得分: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,那么您可以相应地修改模板。但是,在不同的情况下传递很多不同的参数列表会很困难。

答案 2 :(得分:3)

执行此操作的首选方法是使用智能指针容器,例如,std::vector<std::shared_ptr<Foo> >std::vector<std::unique_ptr<Foo> >shared_ptr也可以在Boost和C ++ TR1中找到; std::unique_ptr实际上仅限于C ++ 0x)。

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

答案 3 :(得分:0)

您的计划对内存短缺的恢复程度如何?如果你真的关心这一点,你必须准备好new投掷。如果你不打算处理,我不会担心跳过push_back箍。

在一般情况下,如果内存不足,该程序已经存在可能无法解决的问题,除非它专门设计为在受限的占用空间(嵌入式系统)中永久运行 - 在这种情况下,您必须关心所有这些案例。

如果这适用于您,您可以在您面前进行冗长的代码审核和重新测试。我的猜测是你可以在这里跟随你的团队的练习。

正如其他人所指出的那样,使用vector存储原始指针有其自身的问题,并且此站点上有大量材料,而在其他答案中则指示您使用更安全的模式。