I am writing an application using openFrameworks, but my question is not specific to just oF; rather, it is a general question about C++ vectors in general.
I wanted to create a class that contains multiple instances of another class, but also provides an intuitive interface for interacting with those objects. Internally, my class used a vector of the class, but when I tried to manipulate an object using vector.at(), the program would compile but not work properly (in my case, it would not display a video).
// instantiate object dynamically, do something, then append to vector
vector<ofVideoPlayer> videos;
ofVideoPlayer *video = new ofVideoPlayer;
video->loadMovie(filename);
videos.push_back(*video);
// access object in vector and do something; compiles but does not work properly
// without going into specific openFrameworks details, the problem was that the video would
// not draw to screen
videos.at(0)->draw();
Somewhere, it was suggested that I make a vector of pointers to objects of that class instead of a vector of those objects themselves. I implemented this and indeed it worked like a charm.
vector<ofVideoPlayer*> videos;
ofVideoPlayer * video = new ofVideoPlayer;
video->loadMovie(filename);
videos.push_back(video);
// now dereference pointer to object and call draw
videos.at(0)->draw();
I was allocating memory for the objects dynamically, i.e. ofVideoPlayer = new ofVideoPlayer;
My question is simple: why did using a vector of pointers work, and when would you create a vector of objects versus a vector of pointers to those objects?
std::vectoris like a raw array allocated with new and reallocated when you try to push in more elements than its current size.So, if it contains
Apointers, it’s like if you were manipulating an array ofA*.When it needs to resize (you
push_back()an element while it’s already filled to its current capacity), it will create anotherA*array and copy in the array ofA*from the previous vector.If it contains
Aobjects, then it’s like you were manipulating an array ofA, soAshould be default-constructible if there are automatic reallocations occuring. In this case, the wholeAobjects get copied too in another array.See the difference? The
Aobjects instd::vector<A>can change address if you do some manipulations that requires the resizing of the internal array. That’s where most problems with containing objects instd::vectorcomes from.A way to use
std::vectorwithout having such problems is to allocate a large enough array from the start. The keyword here is “capacity”. Thestd::vectorcapacity is the real size of the memory buffer in which it will put the objects. So, to setup the capacity, you have two choices:1) size your
std::vectoron construction to build all the object from the start , with maximum number of objects – that will call constructors of each objects.2) once the
std::vectoris constructed (but has nothing in it), use itsreserve()function : the vector will then allocate a large enough buffer (you provide the maximum size of the vector). The vector will set the capacity. If youpush_back()objects in this vector orresize()under the limit of the size you’ve provided in thereserve()call, it will never reallocate the internal buffer and your objects will not change location in memory, making pointers to those objects always valid (some assertions to check that change of capacity never occurs is an excellent practice).