Smart pointers in container like std::vector?
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.
C++ Smart Pointers in a Vector container
Your code doesn't contain a memory leak. So your first point is fine.
No, if you don't call shapes.clear()
, there will not be a memory leak since std::vector
's destructor cleans up the container.
I don't know a specific rule about using shared pointers in a container so I'll skip on your third question.
However, I can offer an improvement for creating std::shared_ptr
s:
When you create a shared pointer using its constructor, ie shared_ptr<T>(new T());
, the control block or the structure that holds bookkeeping information about that resource, is created separately from the object it points to. This might cause cache misses a lot. However, if you create a shared_ptr
by using std::make_shared
, the control block is allocated with the object you want, therefore, by keeping them together, you can at least mitigate the cost of cache misses: std::make_shared<T>();
. For example: std::make_shared<Circle>(3)
is equivalent to writing std::shared_ptr<Shape>(new Circle(3))
, but usually better.
Containers vs Smart pointers in C++
I would use the vector. Your pointer version offers basically no improvements over the vector, and you lose a lot of useful functionality. You're most likely going to need to measure the size and iterate your array at some point, with a vector you get this for free, whereas you'd need to implement it yourself for your pointer version; at which point you may as well have just used the vector to begin with.
There may be a performance cost instantiating the vector, but I doubt that it would be a bottleneck for most applications. If you're creating so many vectors that instantiating them is costing you time, you can probably be smarter about managing them (pooling your memory, custom vector allocators, etc). If in doubt, measure.
One example where you might need to use the unique_ptr<>
version might be if you're working with a library written in C where you lose ownership of the array. For example:
std::unique_ptr<unsigned char[]>myArray(
new unsigned char[3 * outputImageHight * outputImageWidth]);
my_c_lib_data_t cLibData;
int result = my_c_lib_set_image(cLibData, myArray);
if (MYLIB_SUCCESS == result)
// mylib successfully took ownership of the char array, so release the pointer.
myArray.release();
If you have the choice though, prefer to use C++ style containers where you can.
C++ Use of smart pointers inside STL containers
If the objects are pointers it is not enough to manage the memory the pointers occupy. You also need to manage what the pointers point to. It is a good idea to store the objects pointed to instead of the pointers (in case of your example std::vector<int>
would be appropriate), however, in case you have polymorphic objects that is not possible.
Performance of smart pointer and raw pointer in containers
This is really opinion-based, but I'll describe the rules of thumb I use.
std:::vector<(struct or class name)>
is my default unless I have specific requirements that are not met by that option. More specifically, it is my go-to option UNLESS at least one of the following conditions are true;
struct or class name
is polymorphic and instances of classes derived fromstruct or class name
need to be stored in the vector.struct or class name
does not comply with the rule of three (before C++11), the rule of five (from C++11), OR the rule of zero- there are SPECIFIC requirements to dynamically manage lifetime of instances of
struct or class name
The above criteria amount to "use std::vector<(struct or class name)>
if struct or class name
meets requirements to be an element of a standard container".
If struct or class name
is polymorphic AND there is a requirement that the vector contain instances of derived classes my default choice is std:::vector<std::unique_ptr<(struct or class name)> >
. i.e. none of the options mentioned in the question.
I will only go past that choice if there are special requirements for managing lifetime of the objects in the vector that aren't met by either std:::vector<(struct or class name)>
or std:::vector<std::unique_ptr<(struct or class name)> >
.
Practically, the above meets the vast majority of real-world needs.
If there is a need for two unrelated pieces of code to have control over the lifetime of objects stored in a vector then (and only then) I will consider std:::vector<std::shared_ptr<(struct or class name)> >
. The premise is that there will be some code that doesn't have access to our vector, but has access to its elements via (for example) being passed a std::shared_ptr<(struct or class name)>
.
Now, I get to the case which is VERY rare in my experience - where there are requirements to manage lifetime of objects that aren't properly handled by std:::vector<(struct or class name)>
, std:::vector<std::unique_ptr<(struct or class name)> >
, or by std:::vector<std::shared_ptr<(struct or class name)> >
.
In that case, and only that case, I will - and only if I'm desperate - use std:::vector<(struct or class name)*>
. This is the situation to be avoided, as much as possible. To give you an idea of how bad I think this option is, I've been known to change other system-level requirements in a quest to avoid this option. The reason I avoid this option like the plague is that it becomes necessary to write and debug EVERY bit of code that explicitly manages the lifetime of each struct or class name
. This includes writing new
expressions everywhere, ensuring every new
expression is eventually matched by a corresponding delete
expression. This option also means there is a need to debug hand-written code to ensure no object is delete
d twice (undefined behaviour) and every object is delete
d once (i.e. avoid leaks). In other words, this option involves lots of effort and - in non-trivial situations - is really hard to get working correctly.
Raw pointer, smart pointer or std::vector for low-level container data in C++
I would use std::vector
because it solves memory allocation, deallocation, indexing, copying, etc.. Unless you will be using "millions" of matrices at the same time, the extra member (capacity
) is probably not relevant.
In any case, optimizing the library for speed is the last thing you want to do -- after you can test the actual speed of your initial implementation. Then you can decide if it is worth spending time to effectively duplicate std::vector
functionality with your own implementation.
C++: how does C++ know to destruct smart pointers inside containers?
Whether the unique_ptr
is on the stack or the heap doesn't really matter. What matters is that it will automatically call delete
on the target pointer when its destructor runs.
When the vector
goes out of scope, the vector's destructor runs. That destroys all contained elements (calling their destructors in the process) and then deallocates its heap buffer.
Handling smart pointers in stl container
Q1: What to do with Foo::at( int ) const
such that you can:
myfoo.at(i)->doSomething(param1, param2, ...);
without transferring ownership out of the vector<unique_ptr<Shape<T>>>
.
A1: Foo::at( int ) const
should return a const std::unique_ptr<Shape<T> >&
:
template < typename T >
const std::unique_ptr<Shape<T> >&
Foo<T>::at( int index ) const
{
return m_Bank[index];
}
Now your can dereference the const unique_ptr
and call any member of Shape
they want (const or non-const). If they accidentally try to copy the unique_ptr
, (which would transfer ownership out of Foo
) they will get a compile time error.
This solution is better than returning a non-const reference to unique_ptr
as it catches accidental ownership transfers out of Foo
. However if you want to allow ownership transfers out of Foo
via at
, then a non-const reference would be more appropriate.
Q2: Furthermore, I found recently on the web an example on how to overload the assignment operator using std::move. I usually follow the Copy-Swap idiom. Which of those two ways for overloading the mentioned operator makes sense for my case?
A2: I'm not sure what ~Foo()
does. If it doesn't do anything, you could remove it, and then (assuming fully conforming C++11) you would automatically get correct and optimal move constructor and move assignment operator (and the proper deleted copy semantics).
If you can't remove ~Foo()
(because it does something important), or if your compiler does not yet implement automatic move generation, you can supply them explicitly, as you have done in your question.
Your move constructor is spot on: Move construct the member.
Your move assignment should be similar (and is what would be automatically generated if ~Foo()
is implicit): Move assign the member:
template < typename T >
Foo<T> & Foo<T>::operator =( Foo<T> && bank )
{
m_Bank = std::move(bank.m_Bank);
return (*this);
}
Your Foo
design lends itself to being Swappable
too, and that is always good to supply:
friend void swap(Foo& x, Foo& y) {x.m_Bank.swap(y.m_Bank);}
Without this explicit swap
, your Foo
is still Swappable
using Foo
's move constructor and move assignment. However this explicit swap
is roughly twice as fast as the implicit one.
The above advice is all aimed at getting the very highest performance out of Foo
. You can use the Copy-Swap idiom in your move assignment if you want. It will be correct and slightly slower. Though if you do be careful that you don't get infinite recursion with swap
calling move assignment and move assignment calling swap
! :-) Indeed, that gotcha is just another reason to cleanly (and optimally) separate swap
and move assignment.
Update
Assuming Shape
looks like this, here is one way to code the move constructor, move assignment, copy constructor and copy assignment operators for Foo
, assuming Foo
has a single data member:
std::vector< std::unique_ptr< Shape > > m_Bank;
...
Foo::Foo(Foo&& other)
: m_Bank(std::move(other.m_Bank))
{
}
Foo::Foo(const Foo& other)
{
for (const auto& p: other.m_Bank)
m_Bank.push_back(std::unique_ptr< Shape >(p ? p->clone() : nullptr));
}
Foo&
Foo::operator=(Foo&& other)
{
m_Bank = std::move(other.m_Bank);
return (*this);
}
Foo&
Foo::operator=(const Foo& other)
{
if (this != &other)
{
m_Bank.clear();
for (const auto& p: other.m_Bank)
m_Bank.push_back(std::unique_ptr< Shape >(p ? p->clone() : nullptr));
}
return (*this);
}
If your compiler supports defaulted move members, the same thing could be achieved with:
Foo(Foo&&) = default;
Foo& operator=(Foo&&) = default;
for the move constructor and move assignment operator.
The above ensures that at all times each Shape
is owned by only one smart pointer/vector/Foo. If you would rather that multiple Foo
s share ownership of Shape
s, then you can have as your data member:
std::vector< std::shared_ptr< Shape > > m_Bank;
And you can default all of move constructor, move assignment, copy constructor and copy assignment.
Container class with vector of smart pointers to abstract base class
1. Declare virtual destructor
class AbstractBase
{
public:
AbstractBase() { }
virtual ~AbstractBase() = default; // this is (defaulted) virtual destructor
virtual std::string toString() = 0;
};
class Derived : public AbstractBase
{
public:
Derived() { }
virtual std::string toString() override { return "Just an example " + std::to_string( _value ); }
private:
int _value;
};
2. Store shared_ptr's in vector
std::vector<std::shared_ptr<AbstractBase>> v = { std::make_shared<Derived>() };
3. Do your stuff and not care about destructors any more.
for (auto i : v)
{
std::cout << i->toString() << std::endl;
}
Related Topics
Best Practices for Use of C++ Header Files
What Is the Use of Volatile Keyword
How to Build Libcxx and Libcxxabi by Clang on Centos 7
What Data Structure, Exactly, Are Deques in C++
Const Unsigned Char * to Std::String
What Optimization Does Move Semantics Provide If We Already Have Rvo
How to Include Correctly -Wl,-Rpath,$Origin Linker Argument in a Makefile
How to Declare Array with Auto
What Is the Modern, Correct Way to Do Type Punning in C++
C++ Object Instantiation VS Assignment
Can You Use Keyword Explicit to Prevent Automatic Conversion of Method Parameters
Meaning of Default Initialization Changed in C++11
Eclipse Indexer Can't Resolve Shared_Ptr
C++ Stack Trace from Unhandled Exception