boost::ptr_container and std::vector<shared_ptr> - c++

After reading timdays answer to this question I am curious about the difference between boost::ptr_container and a std::vector<shared_ptr>. I was under the impression that a boost::ptr_container had ownership over the pointers given to it, and upon release would call the destructors of all the pointers it contained regardless of other references to its inhabitants. Which is contrary to the purpose of a std::vector<shared_ptr>, which after release would only release the pointers themselves if the ref count was 0?
If this is the case (I assume it isn't), why would even the Boost documentation example compare the two as though they are similar in purpose, and why would timday's answer propose a boost::ptr_container when it is very different to the purpose of a std::vector<shared_ptr>.

You are right, the two are widely different.
The first difference, as you noticed, is the ownership semantics. The ownership of items in a Pointer Container is NOT shared. In this regard, a boost::ptr_vector<T> is much closer to a std::vector<std::unique_ptr<T>>.
But this is not the only difference!
unless explicitly stated in the type, a Pointer Container will not contain any null pointer
a Pointer Container has deep copy semantics (using the new_clone method), and can only be copied if the object held is copyable
a Pointer Container has deep const semantics, that is if the container is const then one cannot mutate one of its element.
As for why #timday felt compelled to mention Boost Pointer Container, I think it's because he wanted to broaden the question somewhat. Boost Pointer Container are very much like Smart Pointers that could hold multiple objects, and provide a nicer syntax that containers of pointers in general.
Regarding his comparison to a std::vector< boost::shared_ptr<T> > I think it is simply because this is the traditional way of implementing a vector of pointers in the absence of move semantics (no unique_ptr) since auto_ptr cannot be used in STL container. People just don't know about Pointer Containers most of the time...

There are situations where both can be applied: say a bunch of functions act as clients of container, taking pointers to polymorphic objects out and doing operations on them. If the container outlives all the functions, it can be replaced by a pointer container.
Timday answered the question "What is the difference between the following set of pointer[s]" by pointing out an omission in the list.

Related

Question about move-aware container and std::unique_ptr

As per the document, which says that:
std::unique_ptr is commonly used to manage the lifetime of objects, including:
as the element type in move-aware containers, such as std::vector,
which hold pointers to dynamically-allocated objects (e.g. if
polymorphic behavior is desired) [emphasis mine]
How to understand it in the right way?
Some simple example may helps.
Thanks.
This statement refers to the fact that it was notoriously unsafe to use std::auto_ptr as an element in containers. std::auto_ptr is deprecated already since C++11, but before that there was no move semantics in C++, and auto_ptr was the only "smart" pointer that was really standartized.
std::auto_ptr was similar to std::unique_ptr in that it was destroying the underlying object in the destructor. In contrast to the unique pointer, auto pointer allowed copying: the ownership of the object was transferring to the destination, while the source object was losing the ownership, still pointing to the object. This behavior was unsafe in containers: while you may rely on that the container of auto pointers keeps the ownership, you may easily lose it by simple access to the element (imagine what would happen when you would access the element next time after the actual temporary owner would be destroyed).
It worth mentioning that the STL library provided by Visual Studio C++ 6.0 had a different implementation of auto_ptr: instead of transferring the ownership this "smart" pointer was transferring everything, even the pointer value itself. That meant that the source auto_ptr was becoming empty after the assignment to another "copy" of this pointer. That was safer to store the objects like that in a container, but it caused other problems anyway.
Overall, the problems with usage auto_ptr in containers was [probably] the main reasons why std::auto_ptr was deprecated in C++11.
Returning back to std::unique_ptr: it is safe to use it in containers, and the standard clearly makes an explicit statement referring to the previous problems with std::auto_ptr.

STL containers and non copyable (and non movable) objects

Since STL containers require that all contents be copyable and assignable, what is the prefered idiom when working with non copyable objects?
I can think of two different approaches:
Store (smart) pointers rather than the objects in STL containers.
Get rid of STL containers and implement my own lists (e.g. each object must include a pointer to the next object).
Main drawback of the second approach is implementation of destructors (should the "next" object be destroyed before the current one in a recursive way?)
Since STL containers require that all contents be copyable and assignable, what is the prefered idiom when working with non copyable objects?
Well, actually with C++11 they require the object to be Movable. Only certain operations require them to be Assignable thanks to emplace_* methods.
I can think of two different approaches:
Store (smart) pointers rather than the objects in STL containers.
Get rid of STL containers and implement my own lists (e.g. each object must include a pointer to the next object).
The two approaches are certainly feasible.
In C++11, the STL containers with std::unique_ptr<YourObject> elements in probably the best option. It's standard all the way down. There might be a slight performance issue with the node-based containers since the node and the element they point to will be distinct memory areas; but it's generally imperceptible.
If it is perceptible, or if you cannot use C++11, then you should learn about intrusive containers, which consist in augmenting your objects with hooks so that they can arrange themselves into lists, for example. There is a Boost library for this, obviously: Boost.Intrusive.
Non Movable you said ?
I would honestly challenge most of the designs that argue that an object should not be moved. The only issue with moving is linked to object identity and the potential issues of invalidating pointers to the object being moved from (and thus living dangling pointers behind). This can generally be solved by smart pointers and/or Factory approaches.
I'd choose approach #1: i.e. store smart pointers to objects in STL containers.
Note that it's fine to store non-owning raw pointers in STL containers (e.g. observing raw pointers), but storing owning raw pointers is a "leaktrocity": use shared_ptr or new C++11's unique_ptr instead.
As for #2, writing your own containers from scratch requires lots of time and energy, and I believe you can't match the richness of a full commercial-quality STL library implementation in a reasonable time-frame.

C++ Containers and right hand side

I've read today that you should not use the STL containers for auto_ptr because of
the fact that the auto_ptr deletes it rhs value in the = operator.
So i have 2 question :
1) Does this mean that all classes that have this behavior should not be used in containers?
2) what sort of containers can you use?
1) Does this mean that all classes that have this behavior should not
be used in containers?
Yes indeed, because that is not correct copying behaviour, since the copy is not equal to the source afterwards but destroys the source. This is kind of a broken implementation of move-semantics before C++11, required for the strict unique ownership semantics of std::auto_ptr.
2) what sort of containers can you use?
The real answer is actually, that classes having this behaviour (a copy constructor/assignment destroying its source) should just not exist. And fortunately this is not needed anymore nowadays, since C++11 has proper move-semantics, which realize exactly this destructive copy but in a safe way (simply said, only when the source is really not needed anymore).
Therefore std::auto_ptr is deprecated and should not be used anymore. It has been replaced by std::unique_ptr which is movable but not copyable. But since C++11 containers rather move their elements than copy when appropriate, a std::unique_ptr can be used perfectly inside of standard containers. You just cannot copy the container or fill it with a single object which would require copies of std::unique_ptrs, but those operations should not work anyway, since they are conceptually wrong for unique ownership semantics.
And as a side note, if you actually chose std:auto_ptr for a reason, that is you want unique ownership semantics, then a std::shared_ptr (as suggested by other answers) is plain wrong since it exhibits shared ownership. std::unique_ptr is today's std::auto_ptr. Never spam std::shared_ptrs where std::unique_ptrs (or even raw pointers, but from your question I rule that option out) are appropriate.
Auto pointer has a very strict ownership: it and only it is responsible for the lifetime of the object it points at. If you copy an auto_ptr, you lose the reference to what it pointed at.
The problem is in the way STL containers do their stuff. For example, when you are adding an element, the container might expand to get more memory, which leads to copying all the values to the new memory, which, itself, leads to losing the auto_ptrs.
I think that associative containers might not copy themselves entirely when they allocate additional memory, but I'm absolutely not sure, if someone can confirm it, please post a comment, or just edit my answer. Anyway, you'd better not risk it.
Also note that Auto_ptr is deprecated since C++0x, it is advised to use unique_ptr instead. In your case, std::shared_ptr will probably do the trick, unless you really need unique ownership for those objects of yours.
Exactly.
In general, sequence container elements must be CopyConstructible and Assignable. This means that they require:
public copy constructor
public assignment operator
Asociative containers (set<> and map<>) also must provide strict weak ordering, i.e. operator < must be defined (or a dedicated comparison function).
Chapter 23.1 of C++ standard provides detailed requirements.

Should I use shared_ptr or unique_ptr

I've been making some objects using the pimpl idiom, but I'm not sure whether to use std::shared_ptr or std::unique_ptr.
I understand that std::unique_ptr is more efficient, but this isn't so much of an issue for me, as these objects are relatively heavyweight anyway so the cost of std::shared_ptr over std::unique_ptr is relatively minor.
I'm currently going with std::shared_ptr just because of the extra flexibility. For example, using a std::shared_ptr allows me to store these objects in a hashmap for quick access while still being able to return copies of these objects to callers (as I believe any iterators or references may quickly become invalid).
However, these objects in a way really aren't being copied, as changes affect all copies, so I was wondering that perhaps using std::shared_ptr and allowing copies is some sort of anti-pattern or bad thing.
Is this correct?
I've been making some objects using the pimpl idiom, but I'm not sure whether to used shared_ptr or unique_ptr.
Definitely unique_ptr or scoped_ptr.
Pimpl is not a pattern, but an idiom, which deals with compile-time dependency and binary compatibility. It should not affect the semantics of the objects, especially with regard to its copying behavior.
You may use whatever kind of smart pointer you want under the hood, but those 2 guarantee that you won't accidentally share the implementation between two distinct objects, as they require a conscious decision about the implementation of the copy constructor and assignment operator.
However, these objects in a way really aren't being copied, as changes affect all copies, so I was wondering that perhaps using shared_ptr and allowing copies is some sort of anti-pattern or bad thing.
It is not an anti-pattern, in fact, it is a pattern: Aliasing. You already use it, in C++, with bare pointers and references. shared_ptr offer an extra measure of "safety" to avoid dead references, at the cost of extra complexity and new issues (beware of cycles which create memory leaks).
Unrelated to Pimpl
I understand unique_ptr is more efficient, but this isn't so much of an issue for me, as these objects are relatively heavyweight anyway so the cost of shared_ptr over unique_ptr is relatively minor.
If you can factor out some state, you may want to take a look at the Flyweight pattern.
If you use shared_ptr, it's not really the classical pimpl
idiom (unless you take additional steps). But the real question
is why you want to use a smart pointer to begin with; it's very
clear where the delete should occur, and there's no issue of
exception safety or other to be concerned with. At most,
a smart pointer will save you a line or two of code. And the
only one which has the correct semantics is boost::scoped_ptr,
and I don't think it works in this case. (IIRC, it requires
a complete type in order to be instantiated, but I could be
wrong.)
An important aspect of the pimpl idiom is that its use should be
transparent to the client; the class should behave exactly as if
it were implemented classically. This means either inhibiting
copy and assignment or implementing deep copy, unless the class
is immutable (no non-const member functions). None of the usual
smart pointers implement deep copy; you could implement one, of
course, but it would probably still require a complete type
whenever the copy occurs, which means that you'd still have to
provide a user defined copy constructor and assignment operator
(since they can't be inline). Given this, it's probably not
worth the bother using the smart pointer.
An exception is if the objects are immutable. In this case, it
doesn't matter whether the copy is deep or not, and shared_ptr
handles the situation completely.
When you use a shared_ptr (for example in a container, then look this up and return it by-value), you are not causing a copy of the object it points to, simply a copy of the pointer with a reference count.
This means that if you modify the underlying object from multiple points, then you affect changes on the same instance. This is exactly what it is designed for, so not some anti-pattern!
When passing a shared_ptr (as the comments say,) it's better to pass by const reference and copy (there by incrementing the reference count) where needed. As for return, case-by-case.
Yes, please use them. Simply put, the shared_ptr is an implementation of smart pointer. unique_ptr is an implementation of automatic pointer:

Smart pointers in container like std::vector?

I am learning about smart pointers (std::auto_ptr) and just read here and here that smart pointers (std::auto_ptr) should not be put in containers (i.e. std::vector) because even most compilers won't complain and it might seem correct. There is no rule that says smart pointers won't be copied internally (by vector class for example) and transfer its ownership, then the pointer will become NULL. In the end, everything will be screwed up.
In reality, how often does this happen?
Sometimes I have vectors of pointers and if in the future I decide I want to have a vector of smart pointers what would my options?
I am aware of C++0x and Boost libraries, but for now, I would prefer to stick to a STL approach.
Yes, you really can't use std::auto_ptr with standard containers. std::auto_ptr copies aren't equivalent, and because standard containers (and algorithms) are allowed to copy their elements at will this screws things up. That is, the operation of copying a std::auto_ptr has a meaning other than a mere copy of an object: it means transferring an ownership.
Your options are:
Use the Boost Smart Pointers library. This is arguably your best option.
Use primitive pointers. This is fast and safe, so long as you manage the pointers properly. At times this can be complex or difficult. For example, you'll have to cope with (avoid) double-delete issues on your own.
Use your own reference-counting smart pointer. That'd be silly; use a Boost Smart Pointer.
The problem you are referring to concerns auto_ptr since it moves ownership on copy. shared_ptr and unique_ptr work just fine with containers.
Any type that you use with a standard container template must conform with the requirements for that container. In particular, the type must satisfy the requirements for CopyConstructible and Assignable types.
Many smart pointers do satisfy these requirements and may be used with standard containers but std::auto_ptr is not one of them because copies of std::auto_ptr are not equivalent to the source that they were created or assigned from.
Although some implementations of standard container may work with auto_ptr in some situations it is dangerous to rely on such implementation details.
theoretically you can use std::auto_ptr with STL containers if you completely understand their internal implementation and don't do anything that can lose auto_ptr ownership, but practically it's much more safe to use containers with raw ptrs.
"In reality, how often does this happen?" - is very dangerous question by itself. first of all, STL - it's not a single standard implementation, there're many. Each one can implement containers in different ways, so your highly tuned code to avoid all "auto_ptr in containers" mines can burst switching to another STL implementation. Also, your code maintenance will be highly complicated, any correctly looking change to your code can break your program. how can you remember that or force others maintainers to remember? putting warnings in any place where such changes can occur? impossible.
so, conclusion: it's just a bad idea that brings nothing but headache
For classes that have an auto ptr data member, I always have a clone method that returns a new auto ptr. I then implement an assignment method and copy constructor that call the clone method (and never the default assignment operator of auto ptr). This way you can safely use the class in STL containers.