Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should boost::ptr_vector be used in place std::vector all of the time?

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.

like image 340
jgrowl Avatar asked Sep 03 '09 17:09

jgrowl


2 Answers

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 plain vector for the temporary object - just as you'd use a raw pointer to pass a single object from ptr_vector to a function that takes a pointer, but doesn't store it anywhere.

like image 67
Pavel Minaev Avatar answered Sep 27 '22 22:09

Pavel Minaev


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. :-(

like image 22
Omnifarious Avatar answered Sep 27 '22 22:09

Omnifarious