Just wondering what you think is the best practice regarding vectors in C++.
If I have a class containing a vector member variable. When should this vector be declared a:
- "Whole-object" vector member varaiable containing values, i.e.
vector<MyClass> my_vector;
- Pointer to a vector, i.e
vector<MyClass>* my_vector;
- Vector of pointers, i.e.
vector<MyClass*> my_vector;
- Pointer to vector of pointers, i.e.
vector<MyClass*>* my_vector;
I have a specific example in one of my classes where I have currently declared a vector as case 4, i.e. vector<AnotherClass*>* my_vector;
where AnotherClass is another of the classes I have created.
Then, in the initialization list of my constructor, I create the vector using new:
MyClass::MyClass()
: my_vector(new vector<AnotherClass*>())
{}
In my destructor I do the following:
MyClass::~MyClass()
{
for (int i=my_vector->size(); i>0; i--)
{
delete my_vector->at(i-1);
}
delete my_vector;
}
The elements of the vectors are added in one of the methods of my class. I cannot know how many objects will be added to my vector in advance. That is decided when the code executes, based on parsing an xml-file.
Is this good practice? Or should the vector instead be declared as one of the other cases 1, 2 or 3 ?
When to use which case?
I know the elements of a vector should be pointers if they are subclasses of another class (polymorphism). But should pointers be used in any other cases ?
Thank you very much!!
Complex answer : it depends.
if your vector is shared or has a lifecycle different from the class which embeds it, it might be better to keep it as a pointer. If the objects you're referencing have no (or have expensive) copy constructors , then it's better to keep a vector of pointer. In the contrary, if your objects use shallow copy, using vector of objects prevent you from leaking...
It depends. In doubt whether to choose declaration 1 or 3, think how would you declare a single object of that type. When deciding whether to use number 1 or 2, number 1 is usually the right choice, since a vector is a container with non-fixed size, so you don't need to change it at run time.
In your example, the vector is created when the object is created, and it is destroyed when the object is destroyed. This is exactly the behavior you get when making the
vector
a normal member of the class.Also, in your current approach, you will run into problems when making copies of your object. By default, a pointer would result in a flat copy, meaning all copies of the object would share the same vector. This is the reason why, if you manually manage resources, you usually need The Big Three.
A vector of pointers is useful in cases of polymorphic objects, but there are alternatives you should consider:
boost::ptr_vector
.boost::shared_ptr
, or a vector ofboost::ref
.Deleting all elements in a vector manually is an anti-pattern and violates the RAII idiom in C++. So if you have to store pointers to objects in a
vector
, better use a 'smart pointer' (for exampleboost::shared_ptr
) to facilitate resource destructions.boost::shared_ptr
for example callsdelete
automatically when the last reference to an object is destroyed.There is also no need to allocate
MyClass::my_vector
usingnew
. A simple solution would be:Assuming
whatever
is a smart pointer type, there is no extra work to be done. That's it, all resources are automatically destroyed when the lifetime of aMyClass
instance ends.In many cases you can even use a plain
std::vector<MyClass>
- that's when the objects in the vector are safe to copy.Usually solution 1 is what you want since it’s the simplest in C++: you don’t have to take care of managing the memory, C++ does all that for you (for example you wouldn’t need to provide any destructor then).
There are specific cases where this doesn’t work (most notably when working with polymorphous objects) but in general this is the only good way.
Even when working with polymorphous objects or when you need heap allocated objects (for whatever reason) raw pointers are almost never a good idea. Instead, use a smart pointer or container of smart pointers. Modern C++ compilers provide
shared_ptr
from the upcoming C++ standard. If you’re using a compiler that doesn’t yet have that, you can use the implementation from Boost.Definitely the first!
You use vector for its automatic memory management. Using a raw pointer to a vector means you don't get automatic memory management anymore, which does not make sense.
As for the value type: all containers basically assume value-like semantics. Again, you'd have to do memory management when using pointers, and it's vector's purpose to do that for you. This is also described in item 79 from the book C++ Coding Standards. If you need to use shared ownership or "weak" links, use the appropriate smart pointer instead.