Push_back“动态分配的对象”是否安全?向量?

发布于 2024-10-02 03:43:03 字数 593 浏览 2 评论 0 原文

每当我需要将动态分配的对象添加到向量中时,我都会按以下方式执行此操作:

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> 或者,我可以坚持原来的方式吗?
或者,有更好的方法吗?

Whenever I need to add dynamically allocated object into a vector I've been doing that the following way:

class Foo { ... };

vector<Foo*> v;

v.push_back(new Foo);

// do stuff with Foo in v

// delete all Foo in v

It just worked and many others seem to do the same thing.

Today, I learned vector::push_back can throw an exception. That means the code above is not exception safe. :-( So I came up with a solution:

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

But the problem is that the new way is verbose, tedious, and I see nobody's doing it. (At least not around me...)

Should I go with the new way?
Or, can I just stick with the old way?
Or, is there a better way of doing it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

以可爱出名 2024-10-09 03:43:03

如果您关心的是此操作的异常安全性:

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 是指针类型)。听起来很合理,但合理!=有保证。

因此,除非对这个问题有一个有益的答案:

除了重新分配或构造失败之外,是否允许 std::vector::push_back 因任何原因抛出异常?

此代码取决于实现不做任何太“富有想象力”的事情。如果做不到这一点,您的问题解决方案可以模板化:

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

使用 then 并不太乏味:

struct Bar : Foo { };

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

如果它确实是一个工厂函数而不是 new 那么您可以相应地修改模板。不过,在不同情况下传递许多不同的参数列表会很困难。

If all you care about is exception-safety of this operation:

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.

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:

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

Usage then isn't too tedious:

struct Bar : Foo { };

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

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.

静若繁花 2024-10-09 03:43:03

您的新方法更加异常安全,但您在其他地方看不到它是有原因的。

指针向量仅拥有指针,它不表示所指向对象的所有权。您实际上正在将所有权释放给不“想要”所有权的对象。

大多数人会使用 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 of shared_ptr to express the ownership correctly or use something like boost::ptr_vector. Either of these mean that you don't have to explicitly delete 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 of shared_ptr together with make_shared is much easier to use correctly.

南风起 2024-10-09 03:43:03

执行此操作的首选方法是使用智能指针容器,例如 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 a std::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.

青朷 2024-10-09 03:43:03

您的程序对内存短缺的恢复能力如何?如果你真的关心这个,你也必须准备好抛出 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 the push_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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文