是否应该始终使用 boost::ptr_vector 代替 std::vector ?
这只是我遇到的一个概念性问题。在我当前的项目中,感觉我过度使用了 boost smart_ptr
和 ptr_container
库。我正在创建 boost::ptr_vectors
在许多不同的对象中,并调用 Transfer() 方法将某些指针从一个 boost::ptr_vector 移动到另一个。
我的理解是,清楚地显示堆分配对象的所有权非常重要。
我的问题是,是否需要使用这些 boost 库来创建属于某个对象的堆分配成员,然后在进行任何处理时通过 get()
使用指向这些成员的普通指针。
例如... 一个游戏可能有一组属于它的图块。在 boost::ptr_vector
中创建这些图块可能是有意义的。游戏结束后,这些图块应自动释放。
但是,如果我想暂时将这些 Tiles 放入 Bag 对象中,是否应该在包中创建另一个 boost::ptr_vector
并通过 transfer()
或 我应该创建一个 std::vector
其中 Tiles* 引用 Tiles 在游戏中并将其传递给袋子?
谢谢。
**编辑 我应该指出,在我的示例中,游戏将有一个 Bag 对象作为成员。袋子里只会装满游戏拥有的图块。所以如果没有游戏,袋子就不会存在。
Just a conceptual question that I've been running into. In my current project it feels like I am over-using the boost smart_ptr
and ptr_container
libraries. I was creating boost::ptr_vectors
in many different objects and calling the transfer() method to move certain pointers from one boost::ptr_vector
to another.
It is my understanding that it is important to clearly show ownership of heap allocated objects.
My question is, would it be desirable to use these boost libraries to create heap-allocated members that belong to an object but then use normal pointers to these members via get()
when doing any processing.
For example...
A game might have a collection of Tiles that belong to it. It might make sense to create these tiles in a boost::ptr_vector
. When the game is over these tiles should be automatically freed.
However if I want to put these Tiles in a Bag object temporarily, should I create another boost::ptr_vector
in the bag and transfer the Game's Tiles to the Bag via transfer()
or
should I create a std::vector<Tile*>
where the Tiles*'s reference the Tiles
in the Game and pass that to the Bag?
Thanks.
**Edit
I should point out that in my example The Game would have a Bag object as a member. The Bag would only be filled with Tiles the game owns. So the Bag would not exist without the Game.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
您应该只在有明确所有权转移的情况下使用拥有智能指针和指针容器。对象是否是临时的并不重要,重要的是它是否拥有所有权(因此,前一个所有者是否放弃所有权)。
如果您创建一个临时指针向量只是为了将其传递给其他函数,并且原始
ptr_vector
仍然引用所有这些对象,则不存在所有权转移,因此您应该使用普通的vector< /code> 作为临时对象 - 就像您使用原始指针将单个对象从
ptr_vector
传递到接受指针但不将其存储在任何地方的函数一样。You should only use owning smart pointers and pointer containers where there's clear transfer of ownership. It doesn't matter if the object is temporary or not - all that matters is whether it has ownership or not (and, therefore, whether the previous owner relinquishes ownership).
If you create a temporary vector of pointers just to pass it to some other function, and the original
ptr_vector
still references all those objects, there's no ownership transfer, and therefore you should use plainvector
for the temporary object - just as you'd use a raw pointer to pass a single object fromptr_vector
to a function that takes a pointer, but doesn't store it anywhere.根据我的经验,出现了三种主要的所有权模式。我将它们称为树、DAG 和图。
最常见的是树。父级拥有其子级,而子级又拥有其子级,依此类推。 auto_ptr、scoped_ptr、裸指针和 boost ptr_x 类是您通常在此处看到的内容。在我看来,通常应该避免使用裸指针,因为它们根本不传达所有权语义。
第二个最常见的是 DAG。这意味着您可以拥有共享所有权。父母拥有的孩子也可以是父母拥有的其他孩子的孩子。 TR1 和 boost shared_ptr 模板是这里的主要参与者。当没有循环时,引用计数是一种可行的策略。
第三个最常见的是完整图表。这意味着你可以有周期。有一些策略可以打破这些循环并返回 DAG,但代价是一些可能的错误源。这些一般用TR1或者boost的weak_ptr模板来表示。
使用weak_ptr无法将完整图分解为DAG是一个在C++中不容易解决的问题。唯一好的处理程序是垃圾收集方案。它们也是最通用的,也能够很好地处理其他两种方案。但它们的通用性是有代价的。
在我看来,你不能过度使用 ptr_x 容器类或 auto_ptr ,除非你确实应该使用对象容器而不是指针容器。 shared_ptr 可能被过度使用。仔细考虑一下你是否真的需要 DAG。
当然,我认为人们应该只使用scope_ptrs 的容器而不是boost ptr_x 类,但这必须等待C++0x。 :-(
In my experience, there are three main ownership patterns that crop up. I will call them tree, DAG and graph.
The most common is a tree. The parent owns its children, which in turn owns its children and so on. auto_ptr, scoped_ptr, bare pointers and the boost ptr_x classes are what you typically see here. In my opinion, bare pointers should generally be avoided as they convey no ownership semantics at all.
The second most common is the DAG. This means you can have shared ownership. The children a parent owns may also be the children of other children the parent owns. The TR1 and boost shared_ptr template is the main actor here. Reference counting is a viable strategy when you have no cycles.
The third most common is the full graph. This means that you can have cycles. There are some strategies for breaking those cycles and returning to a DAG at the cost of some possible sources of error. These are generally represented by TR1 or boost's weak_ptr template.
The full graph that can't be broken down into a DAG using weak_ptr is a problem that can't easily be solved in C++. The only good handlers are garbage collection schemes. They are also the most general, capable of handling the other two schemes quite well as well. But their generality comes at a cost.
In my opinion, you can't overuse the ptr_x container classes or auto_ptr unless you really should be using containers of objects instead of containers of pointers. shared_ptr can be overused. Think carefully about whether or not you really need a DAG.
Of course I think people should just be using containers of scope_ptrs instead of the boost ptr_x classes, but that's going to have to wait for C++0x. :-(
最有可能的是,您正在寻找的解决方案是
大多数时候不需要指针。向量已经负责所包含对象的内存管理。这些图块归游戏所有,不是吗?明确这一点的方法是将对象本身放入游戏类中——通常需要指针和动态分配单个对象的唯一情况是,如果您需要 1) 多态性,或 2) 共享所有权。
但指针应该是例外,而不是规则。
Most likely, the solution you're looking for is
There's no need for pointers most of the time. The vector already takes care of memory managements of the contained objects. The tiles are owned by the game, aren't they? The way to make that explicit is to put the objects themselves in the game class -- the only cases where you typically need pointers and dynamically allocated individual objects is if you need 1) polymorphism, or 2) shared ownership.
But pointers should be the exception, not the rule.
boost::ptr_vector
仅用于改进使用指针向量的语义。在您的示例中,如果在使用临时向量集时原始数组可能会被销毁,那么您绝对应该使用shared_ptr
向量来防止它们在仍在使用时被删除。如果不是,那么一个普通的旧指针向量可能是合适的。无论选择 std::vector 还是 boost::ptr_vector 并不重要,除了使用向量的代码看起来有多漂亮之外。boost::ptr_vector
only serves to improve the semantics of using vectors of pointers. If, in your example, the original array could be destroyed while the temporary set of vectors is in use, then you should definitely be using a vector ofshared_ptr
s to prevent them from being deleted while still in use. If not, then a plain old vector of pointers may be appropriate. Whether you choosestd::vector
orboost::ptr_vector
doesn't really matter, except in how nice the code that uses the vector looks.在您的游戏图块示例中,我的第一直觉是创建一个
vector
,而不是vector
或 ptr_vector。然后根据需要使用指向图块的指针,根本不用担心所有权,前提是保存指针的对象不会比图块向量的寿命长。你说过它只会在游戏结束时被摧毁,所以这应该不难。当然,可能有一些原因这是不可能的,例如因为 Tile 是多态基类,并且 Tile 本身并不都属于同一类。但这是C++,不是Java,也不是每个问题都需要动态多态。但即使您确实需要指针,您仍然可以在没有任何所有权语义的情况下复制这些指针,前提是所指向的对象的范围和存储持续时间被理解为比指针的使用范围和持续时间更宽:
In your game tiles example, my first instinct would be to create a
vector<Tile>
, not avector<Tile*>
or a ptr_vector. Then use pointers to the tiles as you please, without worrying about ownership at all, provided that the objects which hold the pointers don't outlive the vector of tiles. You've said it's only destroyed when the game ends, so that shouldn't be difficult.Of course there may be reasons this is not possible, for instance because Tile is a polymorphic base class, and the tiles themselves are not all of the same class. But this is C++, not Java, and not every problem always needs dynamic polymorphism. But even if you do really need pointers, you can still make copies of those pointers without any ownership semantics, provided that the scope and storage duration of the objects pointed to is understood to be wider than the scope and duration of use of the pointer:
如果游戏拥有这些图块,则游戏应对其删除负责。
听起来这个包从来没有真正拥有这些对象,所以它不应该负责删除它们。因此我会在 Game 对象中使用 ptr_vector 。但在包里使用 std::vector 。
注意:我永远不会让任何使用该包的人从包中检索指向图块的指针。他们应该只能从包中检索对图块的引用。
If the Game owns the tiles then the Game is responcable for there deltion.
It sounds like the bag never actually ownes the objects so it should not be responcable for deleting them. Thus I would use ptr_vector within the Game object. But use a std::vector in the bag.
Note: I would never let anybody using the bag retrieve a pointer to a tile from the bag. They should only be able to retrieve a referenceto the tile from the bag.
如果将图块放入袋子中并且有人偷了袋子,您将失去所有图块。因此,Tiles 虽然是暂时的,但它们属于 Bag 的时间很短。我想你应该转移这里的所有权。
但更好的意见是不要搞乱所有权,因为我不明白为什么在这种特殊情况下需要它。如果幕后有什么事情,继续,做出你的选择。
If Tiles are placed in the Bag and someone steals the Bag you lose all tiles. Therefore Tiles, althought temporarly, but they belong to the Bag for a short time. You should transfer ownership here, I suppose.
But the better opinion would be not to mess with ownership, because I don't see why it's needed in this particular case. If there's something behind the scene, go on, make your choice.
我同意使用向量而不是直接跳到堆分配的 T 中是一个很好的本能,但我可以很容易地看到 Tile 是一种类型,而复制构造是昂贵的,这可能会妨碍向量的增长策略的实用性。当然,最好用列表而不是向量来解决这个问题......
I agree that using vector instead of jumping right into heap-allocated T is a good instinct, but I can easily see Tile being a type for while copy construction is expensive, which might preclude vector's growth strategy being practical. Of course, that might be best solved with a list, not a vector...