Topic : STL Newbie Guide
Author : Mumit Khan
Page : << Previous 3  Next >>
Go to page :


set1.end(), print);
    cout << "]" << endl;

    return 0;
}



When you run the program, it should produce the following output.

% ./testc++
List of int*: ( 0 1 4 9 16)
Set of int* : [ 0 1 4 9 16]


Special Note for Borland C++ users: However, Ben Scherrey (scherrey@proteus-tech.com) points out that the OS/2 Borland C++ compiler cannot handle this, and I believe this is due the compiler's lack of support for explicitly calling template destructor. Ben says that the if you overload destroy and construct yourself, it works out. Thanks Ben.

void destroy(X** pointer ) {
    (*pointer)->~X();
}

inline void construct(X** p, const X* value ) {
    new (p) (const X*)(value);
}



Deallocating pointers stored in STL containers
If you create containers of pointers, make sure you deallocate the storage explicitly in the code, especially if the container is on the stack and goes out of scope creating a memory leak. STL containers only copy and delete the storage required to hold the pointer, not the object it's pointing to. You can create templated deleters like the following:

   template <class ForwardIterator, class ForwardIterator>
   void sequence_delete(ForwardIterator first, ForwardIterator last) {
       while (first != last)
    delete *first++;
   }

   template <class ForwardIterator, class ForwardIterator>
   void map_delete(ForwardIterator first, ForwardIterator last) {
       while (first != last)
    delete (*first++).second;
   }

   Map<int, SomeType*, less<int> > mymap_;
   //
   // populate my map with new'd SomeType's.
   //
   map_delete(mymap_.begin(), mymap_.end());



ObjectSpace uses a non-standard release() member to achieve the above.

Who owns the storage?
The following example shows another nasty side effect of storing pointers to things in STL containers.
list<char*> means a list character pointers, NOT strings they point to. less<char*> will compare the pointers, NOT the strings (ie., NOT use strcmp and friends).


   char buf[1024];
   strcpy(buf, "THIS_WOULD_CHANGE_MAGICALLY");
   list<char*> list1;
   list1.push_back(buf);
   ostream_iterator<char*> citer(cout, " ");
   copy(list1.begin(), list1.end(), citer);

   // you should see one string, the one in buf above.

   strcpy(buf, "YIKES!");
   copy(list1.begin(), list1.end(), citer);

   // SURPRISE. your list is changed now.

  

In general, do not use char* as container objects, rather write a simple string class (ask me if you need one) and use that instead.

More gotchas in storing char*
Here's an example of a set of strings that can cause lots of headache.

#include <stl.h>
#include <iostream.h>

int main(int, char*[]) {
    static char* names[] = {"one", "two", "three"};
    set<char*, less<char*> > name_set;
    name_set.insert(names[0]);
    name_set.insert(names[1]);
    name_set.insert(names[2]);

    char buf[256];
    strcpy(buf, "one");
    const char* one = buf;
    set<char*, less<char*> >::const_iterator it = name_set.find(one);
    if (it == name_set.end()) {
        cerr << "No such name `" << one << "' in set!" << endl;
    } else {
        cerr << "Found name `" << one << "' in set." << endl;
    }
    return 0;
}


Example of a pointer wrapper for storing in STL containers
If you have to store pointers in STL containers, especially the sorted collections such as set and map, you might want to wrap the pointers into a simple class that works as a holder for the pointer (who cleans up the memory afterwards?) See here for an example):


#include <stl.h>
#include <iostream.h>

//
// Let's say you want to put pointers to X into multiple STL containers.
//
class X {
public:
    X(int i) : i_(i) { ; }
    X(const X& x) : i_(x.i_) { }
    ~X() { }
    X& operator= (const X& x) { i_ = x.i_; }

    int operator()() const { return i_; }
private:
    int i_;
};

bool operator== (const X& x1, const X& x2) {
    return x1() == x2();
}

bool operator< (const X& x1, const X& x2) {
    return x1() < x2();
}

//
// Define a simple wrapper class to put into STL containers. This one
// simple wraps X.
//
class XPtrWrapper {
public:
    XPtrWrapper(X* x = 0) : x_(x) { }
    XPtrWrapper(const XPtrWrapper& xw) : x_(xw.x_) { }
    ~XPtrWrapper() { }
    XPtrWrapper& operator= (const XPtrWrapper& xw) { x_ = xw.x_; }

    const X* operator() () const { return x_; }
    X* operator() () { return x_; }
private:
    X* x_;
};

bool operator== (const XPtrWrapper& xw1, const XPtrWrapper& xw2) {
    return (xw1.operator()() && xw2.operator()()) ? *xw1() == *xw2() : false;
}

bool operator< (const XPtrWrapper& xw1, const XPtrWrapper& xw2) {
    return (xw1() && xw2()) ? *xw1() < *xw2() : false;
}

void print(const XPtrWrapper& xw) {
    cout << " " << (*xw())();
}

int main(int, char*[]) {
    XPtrWrapper bucket[5];
    for(int i = 0; i < 5; ++i) {
        bucket[i] = XPtrWrapper(new X(i * i));
    }
    random_shuffle(bucket, bucket + 5);

    list<XPtrWrapper> list1;
    copy(bucket, bucket + 5,
        back_insert_iterator<list<XPtrWrapper> > (list1)
    );

    cout << "List of XPtrWrapper: (";
    for_each(list1.begin(), list1.end(), print);
    cout << ")" << endl;

    //
    // now put these XPtrWrappers into a set. Note that I can use
    // greater since I've defined operator> for it.
    //
    set<XPtrWrapper, greater<XPtrWrapper> > set1;
    copy(list1.begin(), list1.end(),
        insert_iterator<set<XPtrWrapper, greater<XPtrWrapper> > >
            (set1, set1.begin())
    );

    cout << "Set of XPtrWrapper : [";
    for_each(set1.begin(), set1.end(), print);
    cout << "]" << endl;

    //
    // now put these integers into a deque.
    //
    deque<XPtrWrapper> deque1;
    copy(list1.begin(), list1.end(),
        back_insert_iterator<deque<XPtrWrapper> > (deque1)
    );

    cout << "Deque of XPtrWrapper : (";
    for_each(deque1.begin(), deque1.end(), print);
    cout << ")" << endl;

    return 0;
}


And the output is:

List of XPtrWrapper: ( 4 0 16 1 9)
Set of XPtrWrapper : [ 16 9 4 1 0]
Deque of XPtrWrapper : ( 4 0 16 1 9)

How do I store derived objects in STL containers?
Consider a CAD application: you have lots of objects on a screen that all derive from the same base object. How would you store the derived objects in an STL container? Let's assume all derived objects have a set of virtual functions (and use RTTI if you have it). I've done it 3 different ways:

Store the pointer itself in the container. You have to explicitly deallocate the memory later on of course. Also, not all STL implementations seem to handle storage of pointers uniformly, so I would suggest using a wrapper shown below.
Hard-coded wrapper that takes a pointer to the base class
Templated pointer wrapper that takes a pointer to the base class

Hard-coded wrapper that takes a pointer to the base class
The following example shows 2 classes derived from Base, derived1 and derived2 and a wrapper BaseWrapper. The wrapper class assumes that the base class provides a virtual clone facility and does the memory management.
Note: After the new'd Base derivative is passed to the wrapper, it owns it and deletes it in the destructor.

#include <stl.h>
#include <string.h>
#include <iostream.h>

//
// abstract base class
//
class Base {
public:
    const char* typename() const { return typename_; }
    virtual Base* clone() const = 0;
    virtual void identify(ostream& os) const = 0;
    virtual ~Base();

public:
    static int count;

protected:
    Base(const char* typename);
    Base(const Base& base);

private:
    char* typename_;
};

Base::Base(const char* typename) {
    const char* tname = (typename) ? typename : "unknown";
    strcpy(typename_ = new char[strlen(tname) + 1], tname);
    ++count;
}

Base::Base(const Base& base) {
    strcpy(
        typename_ = new char[strlen(base.typename_) + 1], base.typename_
    );
    ++count;
}

Base::~Base() {
    delete[] typename_;
    --count;
}

//
// First derived class.
//
class Derived1 : public Base {
public:
    Derived1(int data) : Base("derived1"),


Page : << Previous 3  Next >>