Vectors are handy things for simple lists where you want random access (eg. for sorting, shuffling and so on).
The example below illustrates various things you can do with vectors. I have used vectors of strings so I can also play with case-independent sorts, sort descending, appending to each item, and so on.
Notice the difference the case-independent sort makes on the words "the" and "The". For case-independence they are together, otherwise they are at opposite ends of the sentence.
Output
The example also demonstrates "back insertion". One of the nice things about vectors is they can grow as large as you like without having to decide in advance how large that will be, however if you are doing that you need to tell it to insert (and grow) the vector. That is what the back insertion does. Another way would have been:
In this case the constructor for the vector is passed the number of items, so that the vector is initialised to the correct size in advance. Once that is done, you can just put the elements into their respective places.
Personally I think that is a bit tedious.
Another approach would be to forget the back inserter and just do a push_back, like this:
I still think that is more coding than the original.
Another approach again is to use a C array, and copy into the STL one. This probably looks the neatest ...
These examples just show there is more than one way of achieving a result. :)
The back_insert_iterator, back_inserter and push_back, are all variants of the same thing. At the end of the day back_inserter calls push_back, and the back_insert_iterator implements an operator* which also calls push_back. It just depends on which you think is the easiest to code and debug.
In fact, if you look at the way the back_insert_iterator is implemented, the ++ is a no-op, so this would have worked:
However this is probably not good coding style, as it isn't very obvious from looking at it that the iterator is really progressing to different elements.
The example below illustrates various things you can do with vectors. I have used vectors of strings so I can also play with case-independent sorts, sort descending, appending to each item, and so on.
Notice the difference the case-independent sort makes on the words "the" and "The". For case-independence they are together, otherwise they are at opposite ends of the sentence.
// disable warnings about long names
#ifdef WIN32
#pragma warning( disable : 4786)
#endif
#include <string>
#include <vector>
#include <iostream>
#include <functional>
#include <iterator>
#include <algorithm>
using namespace std;
// case-independent (ci) string less_than
// returns true if s1 < s2
struct ci_less : binary_function<string, string, bool>
{
// case-independent (ci) compare_less binary function
struct nocase_compare : public binary_function<unsigned char,unsigned char,bool>
{
bool operator() (const unsigned char& c1, const unsigned char& c2) const
{ return tolower (c1) < tolower (c2); }
};
bool operator() (const string & s1, const string & s2) const
{
return lexicographical_compare
(s1.begin (), s1.end (), // source range
s2.begin (), s2.end (), // dest range
nocase_compare ()); // comparison
}
}; // end of ci_less
void showv (const string s, const vector<string> & v)
{
cout << s << endl;
copy(v.begin(), v.end(), ostream_iterator<string>(cout, " "));
cout << endl << endl;
} // end of showv
int main (void)
{
// vector of strings
vector<string> v;
// make a back insert iterator to safely add to the back of the vector
back_insert_iterator<vector<string> > i(v);
// Insert items into the vector.
// Strictly speaking, the "++" is not needed, as the
// assignment to the iterator is what advances it.
*i++ = "The";
*i++ = "quick";
*i++ = "brown";
*i++ = "fox";
*i++ = "jumped";
*i++ = "over";
*i++ = "the";
*i++ = "lazy";
*i++ = "dog";
showv ("initial vector...", v);
// sort ascending
sort (v.begin (), v.end ());
showv ("after sort (ascending)...", v);
// sort descending by using greater function to compare less
sort (v.begin (), v.end (), greater<string> ());
showv ("after sort (descending)...", v);
// sort case-independent by using our own compare-less
sort (v.begin (), v.end (), ci_less ());
showv ("after case-independent sort (ascending)...", v);
// reverse that by doing a "not" on the result
sort (v.begin (), v.end (), not2 (ci_less ()));
showv ("after case-independent sort (descending)...", v);
// shuffle the words
random_shuffle (v.begin (), v.end ());
showv ("after shuffle...", v);
// rotate them
rotate (v.begin (), v.begin () + 1, v.end ());
showv ("after rotate 1 to the left...", v);
// reverse their order
reverse (v.begin (), v.end ());
showv ("after reverse...", v);
// add "(" to the beginning of each one
transform (v.begin (), v.end (),
v.begin (),
bind1st (plus<string> (), "("));
// add ")" to the end of each one
transform (v.begin (), v.end (),
v.begin (),
bind2nd (plus<string> (), ")"));
showv ("after transforms...", v);
return 0;
} // end of main
Output
initial vector...
The quick brown fox jumped over the lazy dog
after sort (ascending)...
The brown dog fox jumped lazy over quick the
after sort (descending)...
the quick over lazy jumped fox dog brown The
after case-independent sort (ascending)...
brown dog fox jumped lazy over quick the The
after case-independent sort (descending)...
The the quick over lazy jumped fox dog brown
after shuffle...
the dog quick over jumped fox The lazy brown
after rotate 1 to the left...
dog quick over jumped fox The lazy brown the
after reverse...
the brown lazy The fox jumped over quick dog
after transforms...
(the) (brown) (lazy) (The) (fox) (jumped) (over) (quick) (dog)
The example also demonstrates "back insertion". One of the nice things about vectors is they can grow as large as you like without having to decide in advance how large that will be, however if you are doing that you need to tell it to insert (and grow) the vector. That is what the back insertion does. Another way would have been:
vector<string> v (9);
v [0] = "The";
v [1] = "quick";
v [2] = "brown";
v [3] = "fox";
v [4] = "jumped";
v [5] = "over";
v [6] = "the";
v [7] = "lazy";
v [8] = "dog";
In this case the constructor for the vector is passed the number of items, so that the vector is initialised to the correct size in advance. Once that is done, you can just put the elements into their respective places.
Personally I think that is a bit tedious.
Another approach would be to forget the back inserter and just do a push_back, like this:
vector<string> v;
v.push_back ("The");
v.push_back ("quick");
v.push_back ("brown");
v.push_back ("fox");
v.push_back ("jumped");
v.push_back ("over");
v.push_back ("the");
v.push_back ("lazy");
v.push_back ("dog");
I still think that is more coding than the original.
Another approach again is to use a C array, and copy into the STL one. This probably looks the neatest ...
// vector of strings
vector<string> v;
// initial values
char * s [] = { "The", "quick", "brown", "fox", "jumped", "over",
"the", "lazy", "dog" };
// copy into vector
copy (s, &s [ sizeof s / sizeof s [0] ], back_inserter (v));
These examples just show there is more than one way of achieving a result. :)
The back_insert_iterator, back_inserter and push_back, are all variants of the same thing. At the end of the day back_inserter calls push_back, and the back_insert_iterator implements an operator* which also calls push_back. It just depends on which you think is the easiest to code and debug.
In fact, if you look at the way the back_insert_iterator is implemented, the ++ is a no-op, so this would have worked:
back_insert_iterator<vector<string> > i(v);
*i = "The";
*i = "quick";
*i = "brown";
*i = "fox";
*i = "jumped";
*i = "over";
*i = "the";
*i = "lazy";
*i = "dog";
However this is probably not good coding style, as it isn't very obvious from looking at it that the iterator is really progressing to different elements.