Push_back“动态分配的对象”是否安全?向量?
每当我需要将动态分配的对象添加到向量中时,我都会按以下方式执行此操作:
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
但问题是新方法冗长、乏味,而且我看到没有人这样做。(至少在我周围没有......)
我应该采用新方法吗?< br>
或者,我可以坚持原来的方式吗?
或者,有更好的方法吗?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
如果您关心的是此操作的异常安全性:
负责释放其内容所指向的对象的向量问题是另一回事,我相信您会得到很多关于此的建议;-)
编辑:嗯,我本来打算引用该标准,但实际上我找不到必要的保证。我正在寻找的是
push_back
不会抛出,除非(a)它必须重新分配(我们知道它不会因为容量),或(b) T 的构造函数抛出(我们知道它不会抛出,因为 T 是指针类型)。听起来很合理,但合理!=有保证。因此,除非对这个问题有一个有益的答案:
除了重新分配或构造失败之外,是否允许 std::vector::push_back 因任何原因抛出异常?
此代码取决于实现不做任何太“富有想象力”的事情。如果做不到这一点,您的问题解决方案可以模板化:
使用 then 并不太乏味:
如果它确实是一个工厂函数而不是
new
那么您可以相应地修改模板。不过,在不同情况下传递许多不同的参数列表会很困难。If all you care about is exception-safety of this operation:
The issue of a vector having responsibility for freeing the objects pointed to by its contents is a separate thing, I'm sure you'll get plenty of advice about that ;-)
Edit: hmm, I was going to quote the standard, but I actually can't find the necessary guarantee. What I'm looking for is that
push_back
will not throw unless either (a) it has to reallocate (which we know it won't because of the capacity), or (b) a constructor of T throws (which we know it won't since T is a pointer type). Sounds reasonable, but reasonable != guaranteed.So, unless there's a beneficial answer over on this question:
Is std::vector::push_back permitted to throw for any reason other than failed reallocation or construction?
this code depends on the implementation not doing anything too "imaginative". Failing that, your solution from the question can be templated up:
Usage then isn't too tedious:
If it's really a factory function rather than
new
then you could modify the template accordingly. Passing a lot of different parameter lists in different situations would be difficult, though.您的新方法更加异常安全,但您在其他地方看不到它是有原因的。
指针向量仅拥有指针,它不表示所指向对象的所有权。您实际上正在将所有权释放给不“想要”所有权的对象。
大多数人会使用
shared_ptr
的向量
来正确表达所有权,或者使用诸如boost::ptr_vector
之类的东西。其中任何一个都意味着您不必显式删除
您正在存储其指针的对象,这很容易出错,并且在程序中的其他点可能会出现异常“危险”。编辑:在插入
ptr_vector
时仍然需要非常小心。不幸的是,采用原始指针的push_back
提供了强有力的保证,这意味着要么插入成功,要么(实际上)什么也没有发生,因此传入的对象既不会被接管也不会被销毁。按值获取智能指针的版本被定义为在调用强保证版本之前调用.release()
,这实际上意味着它可能会泄漏。将
shared_ptr
的向量
与make_shared
一起使用更容易正确使用。Your new way is more exception safe but there is a reason that you don't see it done anywhere else.
A
vector
of pointers only owns the pointers, it doesn't express ownership of the pointed-to objects. You are effectively releasing ownership to an object that doesn't "want" ownership.Most people will use a
vector
ofshared_ptr
to express the ownership correctly or use something likeboost::ptr_vector
. Either of these mean that you don't have to explicitlydelete
the objects whose pointers you are storing which is error prone and potentially exception 'dangerous' at other points in the program.Edit: You still have to be very careful with insertion into
ptr_vector
. Unfortunately,push_back
taking a raw pointer provides the strong guarantee which means that either insertion succeeds or (effectively) nothing happens, so the object passed in is neither taken over nor destroyed. The version taking a smart pointer by value is defined as calling.release()
before calling the strongly guaranteed version which effectively means that it can leak.Using a
vector
ofshared_ptr
together withmake_shared
is much easier to use correctly.执行此操作的首选方法是使用智能指针容器,例如 std::vector 。 > 或
std::vector >
(shared_ptr
也可以在 Boost 和 C++ TR1 中找到;std::unique_ptr
实际上仅限于 C++0x)。另一种选择是使用拥有动态对象的容器,例如 Boost Pointer Containers 库提供的容器。
The preferred way to do this is to use a container of smart pointers, for example, a
std::vector<std::shared_ptr<Foo> >
or astd::vector<std::unique_ptr<Foo> >
(shared_ptr
can be found in Boost and C++ TR1 as well;std::unique_ptr
is effectively restricted to C++0x).Another option is to use a container that owns dynamic objects, like those containers provided by the Boost Pointer Containers library.
您的程序对内存短缺的恢复能力如何?如果你真的关心这个,你也必须准备好抛出 new 。如果您不打算处理这个问题,我也不会担心跳过
push_back
的障碍。在一般情况下,如果内存不足,程序可能已经存在无法克服的问题,除非它专门设计为在有限的占用空间(嵌入式系统)中永远运行 - 在这种情况下,您确实必须关心所有这些情况。
如果这适用于您,您可能需要经历漫长的代码审查和重新测试周期。不过,我的猜测是,您可以遵循团队的做法。
正如其他人指出的那样,使用向量来存储原始指针有其自身的问题,并且该网站和其他答案中有大量材料可以指导您采用更安全的模式。
How resilient to memory shortage is your program? If you really care about this you have to be prepared for
new
to throw as well. If you aren't going to handle that, I would not worry about jumping through thepush_back
hoops.In the general case, if you run out of memory, the program already has likely insurmountable problems unless it's specifically designed to run forever in a constrained footprint (embedded systems) - in that case you do have to care about all of these cases.
If that applies to you, you could have a lengthy code review and retest cycle ahead of you. My guess is that you are OK to follow your team's practice here, though.
As others have pointed out, using
vector
to store raw pointers has its own problems, and there is a wealth of material on this site and in the other answers to direct you to a safer pattern.