Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |
In the previous example, at the end of main( )
it was necessary to move through the whole list and delete all the Shape
pointers:
for(Iter j = shapes.begin(); j != shapes.end(); j++)
delete *j;
STL containers make sure that each object they
contain has its destructor called when the container itself is destroyed.
Pointers, however, have no destructor, so we have to delete them
ourselves.
This highlights what could be seen as an oversight in the
STL: there s no facility in any of the STL containers to automatically delete
the pointers they contain, so you must do it manually. It s as if the
assumption of the STL designers was that containers of pointers weren t an
interesting problem, but that s not the case.
Automatically deleting a pointer turns out to be problematic
because of the multiple membership problem. If a container holds a
pointer to an object, it s not unlikely that pointer could also be in another
container. A pointer to an Aluminum object in a list of Trash
pointers could also reside in a list of Aluminum pointers. If that
happens, which list is responsible for cleaning up that object that is, which
list owns the object?
This question is virtually eliminated if the object rather
than a pointer resides in the list. Then it seems clear that when the list is
destroyed, the objects it contains must also be destroyed. Here, the STL
shines, as you can see when creating a container of string objects. The
following example stores each incoming line as a string in a vector<string>:
//: C07:StringVector.cpp
// A vector of strings.
#include <fstream>
#include <iostream>
#include <iterator>
#include <sstream>
#include <string>
#include <vector>
#include "../require.h"
using namespace std;
int main(int argc, char* argv[]) {
const char* fname = "StringVector.cpp";
if(argc > 1) fname = argv[1];
ifstream in(fname);
assure(in, fname);
vector<string> strings;
string line;
while(getline(in, line))
strings.push_back(line);
// Do something to the strings...
int i = 1;
vector<string>::iterator w;
for(w = strings.begin(); w !=
strings.end(); w++) {
ostringstream ss;
ss << i++;
*w = ss.str() + ": " + *w;
}
// Now send them out:
copy(strings.begin(), strings.end(),
ostream_iterator<string>(cout,
"\n"));
// Since they aren't pointers, string
// objects clean themselves up!
} ///:~
Once the vector<string> called strings
is created, each line in the file is read into a string and put in the vector:
while(getline(in, line))
strings.push_back(line);
The operation that s being performed on this file is to add
line numbers. A stringstream provides easy conversion from an int
to a string of characters representing that int.
Assembling string objects is quite easy, since operator+
is overloaded. Sensibly enough, the iterator w can be dereferenced to
produce a string that can be used as both an rvalue and an lvalue:
*w = ss.str() + ": " + *w;
You may be surprised that you can assign back into the
container via the iterator, but it s a tribute to the careful design of the
STL.
Because the vector<string> contains the
objects, two things are worthy of note. First, as explained before, you don t
need to explicitly clean up the string objects. Even if you put
addresses of the string objects as pointers into other
containers, it s clear that strings is the master list and maintains
ownership of the objects.
Second, you are effectively using dynamic object creation,
and yet you never use new or delete! It s all taken care of for
you by the vector because it stores copies of the objects you
give it. Thus your coding is significantly cleaned up.
Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |