Confusion about copying and writing and shared_ptr

I searched the web and read the Boost documentation on shared_ptr . There is an answer to SO that says that shared_ptr for Copy-On-Write (COW) sucks, and that TR! deleted it from row libraries. Most SO tips say use shared_ptr , not regular pointers.

The documentation also talks about using std::unique() to create a COW pointer, but I have not found any examples.

Does it mean that you have a smart pointer that does COW for you, or that your object uses the new shared_ptr for the cloned object and then modifies the cloned object?

Example: recipes and ingredients

 struct Nutrients; struct Ingredient { Ingredient(const std::string& new_title = std::string("")) : m_title(new_title) { ; } std::string m_title; Nutrients ing_nutrients; }; struct Milk : public Ingredient : Ingredient("milk") { ; } struct Cream : public Ingredient : Ingredient("cream") { ; } struct Recipe { std::vector< boost::shared_ptr<Ingredient> > m_ingredients; void append_ingredient(boost::shared_ptr<Ingredient> new_ingredient) { m_ingredients.push_back(new_ingredient); return; } void replace_ingredient(const std::string& original_ingredient_title, boost::shared_ptr<Ingredient> new_ingredient) { // Confusion here } }; int main(void) { // Create an oatmeal recipe that contains milk. Recipe oatmeal; boost::shared_ptr<Ingredient> p_milk(new Milk); oatmeal.add_ingredient(p_milk); // Create a mashed potatoes recipe that contains milk Recipe mashed_potatoes; mashed_potatoes.add_ingredient(p_milk); // Now replace the Milk in the oatmeal with cream // This must not affect the mashed_potatoes recipe. boost::shared_ptr<Ingredient> p_cream(new Cream); oatmeal.replace(p_milk->m_title, p_cream); return 0; } 

The confusion is how to replace the β€œMilk” in the oatmeal recipe with Cream and not affect the mashed_potatoes recipe.

My algorithm:

 locate pointer to `Milk` ingredient in the vector. erase it. append `Cream` ingredient to vector. 

What will the COW pointer look like here?

Note. I am using MS Visual Studio 2010 on Windows NT, Vista and 7.

+7
source share
2 answers

There are a few questions here, so bear with me if I don’t address them in the order you expect.

Most SO tips say use shared_ptr, not regular pointers.

Yes and no. Many SO users, unfortunately, recommend shared_ptr as if it were a silver bullet to solve all memory management issues. This is not . Most tips say that they do not use bare pointers, which is significantly different.

The real advice is to use smart managers : smart pointers ( unique_ptr , scoped_ptr , shared_ptr , auto_ptr ), smart containers ( ptr_vector , ptr_map ) or custom solutions for complex tasks (based on Boost.MultiIndex, using intrusive counters, etc.).

You must choose a smart manager to use, as appropriate. Most notably, if you do not need to share ownership of the object, then you should not use shared_ptr .

What is COW?

COW (Copy-On-Write) is about sharing data to "save" memory and simplify copying ... without changing the semantics of the program.

From the user's point of view, whether std::string COW is used or not does not matter. When a row changes, all other rows are not affected.

The idea behind COW is as follows:

  • If you are the sole owner of the data, you can change it.
  • If you do not, you must copy it and then use a copy

It looks like shared_ptr , so why not?

This is similar, but both are designed to solve different problems, and as a result they are slightly different.

The problem is that since shared_ptr designed to run smoothly regardless of whether it is shared or not, it is difficult for COW to run the "if only owner" test. Remarkably, the weak_ptr interaction weak_ptr it difficult.

Perhaps, obviously. The key is not to leak shared_ptr in general, and not to use weak_ptr (they are still useless for COW).

Does it matter?

No, not at all. It has been proven that COW is not so great. In most cases, it’s micro-optimization ... and micro-pessimization right away. You can get rid of some memory (although it only works if you do not copy large objects), but you complicate the algorithm, which can slow down the execution (you enter tests).

My advice is not to use COW. And do not use those shared_ptr .


Personally, I would like to:

  • use boost::ptr_vector<Ingredient> , not std::vector< boost::shared_ptr<Ingredient> > (you don't need sharing)
  • create an IngredientFactory that will create (and manage) the ingredients and return Ingredient const& Factory must survive any Receipt .

EDIT : after a comment by Xeo, it seems the last element ( IngredientFactory ) is pretty concise ...

In the case of an IngredientFactory the Receipt object will contain std::vector<Ingredient const*> . Note the raw pointer:

  • Receipt not responsible for the memory, but gets access to it
  • there is an implicit guarantee that the specified object will remain valid longer than the Receipt object

It is good to use raw (bare) pointers if you treat them as if you were referencing. You just need to beware of potential invalidity, and you are offered the opportunity to reinstall them if you wish, and you trust the provider to take care of lifecycle / memory management aspects.

+11
source

You have nothing to worry about. Each Recipe object has its own vector , so changing it will not affect the other, although both of them contain pointers to the same objects. The mashed potato recipe will only be affected if you change the contents of the object pointed to by p_milk , but you do not. You are oatmeal.m_ingredients object, which has absolutely nothing to do with mashed_potatoes.m_ingredients . These are two completely independent instances of vector .

+1
source

All Articles