MFC Programmer's SourceBook : Thinking in C++
Bruce Eckel's Thinking in C++, 2nd Ed Contents | Prev | Next

Containers of strings

In the prior 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; 

This highlights what could be seen as a flaw 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 by hand. It’s as if the assumption of the STL designers was that containers of pointers weren’t an interesting problem, although I assert that it is one of the more common things you’ll want to do.

Automatically deleting a pointer turns out to be a rather aggressive thing to do 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>:

//: C20:StringVector.cpp
// A vector of strings
#include <string>
#include <vector>
#include <fstream>
#include <iostream>
#include <iterator>
#include <sstream>
#include "../require.h"
using namespace std;

int main(int argc, char* argv[]) {
  requireArgs(argc, 1);
  ifstream in(argv[1]);
  assure(in, argv[1]);
  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;

The fact that you can assign back into the container via the iterator may seem a bit surprising at first, but it’s a tribute to the careful design of the STL.

Because the vector<string> contains the objects themselves, a number of interesting things take place. First, no cleanup is necessary. Even if you were to 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! That’s because, somehow, it’s all taken care of for you by the vector (this is non-trivial. You can try to figure it out by looking at the header files for the STL – all the code is there – but it’s quite an exercise). Thus your coding is significantly cleaned up.

The limitation of holding objects instead of pointers inside containers is quite severe: you can’t upcast from derived types, thus you can’t use polymorphism. The problem with upcasting objects by value is that they get sliced and converted until their type is completely changed into the base type, and there’s no remnant of the derived type left. It’s pretty safe to say that you never want to do this.

Contents | Prev | Next


Go to CodeGuru.com
Contact: webmaster@codeguru.com
© Copyright 1997-1999 CodeGuru