C and C++ Programming Resources

Custom Search

Generic containers

Containers of strings

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;

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 by hand. It?s as if the assumption of the STL designers was that containers of pointers weren?t an interesting problem.
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>:

// 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[]) {
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 themselves, a number of interesting things take place. First, no explicit cleanup of the string objects on your part 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 because it stores copies of the objects you give it. 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 safe to say that you never want to do this.

Pages: [Page - 1] [Page - 2] [Page - 3] [Page - 4] [Page - 5] [Page - 6] [Page - 7] [Page - 8] [Page - 9] [Page - 10] [Page - 11] [Page - 12] [Page - 13]

Tags: , ,

There are 1 Comment to this post. You can follow any responses to this entry through the RSS 2.0 feed. You can skip to the end and leave a response or TrackBack from your own site.

  • Jimson says:

    Sorry to disappoint you, but this is actually bull. STL containers are not meant to be used as base classes. There are no virtual functions to overwrite, so you can’t alter the functionality. Data members are private, so no luck there either – these two points are make container inheritance questionable, but the fact that no virtual destructor is offered makes it just plain wrong. Deleting a derived class via a pointer to the container base class will result in undefined behaviour.

    If you want to add data members, just use composition. If you want to add functionality, write a new algorithm, but don’t inherit from STL containers.

    You might want to look this issue up, the internet is actually full of advice on this, as many novice STL users make this mistake…


Leave a Reply

You must be logged in to post a comment.