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


data_(data) { }
    Derived1(const Derived1& d) : Base("derived1"), data_(d.data()) { }
    virtual ~Derived1()        { }
    virtual Base* clone() const { return new Derived1(*this); }
    virtual void identify(ostream& os) const;
    int data() const { return data_; }
private:
    int data_;
};

virtual void Derived1::identify(ostream& os) const {
    os << "(" << typename() << " " << data() << ")";
}

//
// Second derived class.
//
class Derived2 : public Base {
public:
    Derived2(int data) : Base("derived2"), data_(data) { }
    Derived2(const Derived2& d) : Base("derived2"), data_(d.data()) { }
    virtual ~Derived2()        { }
    virtual Base* clone() const { return new Derived2(*this); }
    virtual void identify(ostream& os) const;
    int data() const { return data_; }
private:
    int data_;
};

virtual void Derived2::identify(ostream& os) const {
    os << "(" << typename() << " " << data() << ")";
}

//
// now define the pointer wrapper.
//
class BaseWrapper {
public:
    BaseWrapper(Base* base_ptr = 0) : base_ptr_(base_ptr) { }
    BaseWrapper(const BaseWrapper& bw) {
        base_ptr_ = bw() ? bw()->clone() : 0;
    }
    ~BaseWrapper() { delete base_ptr_; }
    const Base* operator()()  const { return base_ptr_; }
    Base* operator()()  { return base_ptr_; }
    BaseWrapper& operator= (const BaseWrapper& bw) {
        delete base_ptr_;
        base_ptr_ = bw()->clone();
    }
private:
    Base* base_ptr_;
};

bool operator== (const BaseWrapper& bw1, const BaseWrapper& w2) {
    return false;
}

bool operator< (const BaseWrapper& bw1, const BaseWrapper& w2) {
    return false;
}

//
// end of class defs.
//

// define static members.
int Base::count = 0;

int main(int, char*[]) {
    list<BaseWrapper> list1;
    list1.push_back(BaseWrapper(new Derived1(101)));
    list1.push_back(BaseWrapper(new Derived2(201)));
    list1.push_back(BaseWrapper(new Derived2(202)));
    list1.push_back(BaseWrapper(new Derived1(102)));
    list1.push_back(BaseWrapper(new Derived2(203)));

    list<BaseWrapper>::const_iterator it = list1.begin();
    for(; it != list1.end(); ++it) {
        const BaseWrapper& bw = *it;
        bw()->identify(cerr);
        cerr << " ";
    }
    cerr << endl << endl;

    return 0;
}


And here's the output:


(derived1 101) (derived2 201) (derived2 202) (derived1 102) (derived2 203)


Templated pointer wrapper that takes a pointer to the base class
The following example shows 2 classes derived from Base, derived1 and derived2 and a templated wrapper Wrapper<T>. 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"), data_(data) { }
    Derived1(const Derived1& d) : Base("derived1"), data_(d.data()) { }
    virtual ~Derived1()        { }
    virtual Base* clone() const { return new Derived1(*this); }
    virtual void identify(ostream& os) const;
    int data() const { return data_; }
private:
    int data_;
};

virtual void Derived1::identify(ostream& os) const {
    os << "(" << typename() << " " << data() << ")";
}

//
// Second derived class.
//
class Derived2 : public Base {
public:
    Derived2(int data) : Base("derived2"), data_(data) { }
    Derived2(const Derived2& d) : Base("derived2"), data_(d.data()) { }
    virtual ~Derived2()        { }
    virtual Base* clone() const { return new Derived2(*this); }
    virtual void identify(ostream& os) const;
    int data() const { return data_; }
private:
    int data_;
};

virtual void Derived2::identify(ostream& os) const {
    os << "(" << typename() << " " << data() << ")";
}

//
// now define a templated pointer wrapper. The class must support the
// clone() method.
//
template <class T>
class PtrWrapper {
public:
    PtrWrapper(T* t_ptr = 0) : t_ptr_(t_ptr) { }
    PtrWrapper(const PtrWrapper<T>& w) {
        t_ptr_ = w() ? w()->clone() : 0;
    }
    ~PtrWrapper() { delete t_ptr_; }
    const T* operator()()  const { return t_ptr_; }
    T* operator()()  { return t_ptr_; }
    PtrWrapper<T>& operator= (const PtrWrapper<T>& w) {
        delete t_ptr_;
        t_ptr_ = w()->clone();
        return *this;
    }
private:
    T* t_ptr_;
};

template <class T>
bool operator== (const PtrWrapper<T>& w1, const PtrWrapper<T>& w2) {
    return false;
}

template <class T>
bool operator< (const PtrWrapper<T>& w1, const PtrWrapper<T>& w2) {
    return false;
}

//
// end of class defs.
//

// define static members.
int Base::count = 0;

int main(int, char*[]) {
    list<PtrWrapper<Base> > list1;
    list1.push_back(PtrWrapper<Base> (new Derived1(101)));
    list1.push_back(PtrWrapper<Base> (new Derived2(201)));
    list1.push_back(PtrWrapper<Base> (new Derived2(202)));
    list1.push_back(PtrWrapper<Base> (new Derived1(102)));
    list1.push_back(PtrWrapper<Base> (new Derived2(203)));

    list<PtrWrapper<Base> >::const_iterator it = list1.begin();
    for(; it != list1.end(); ++it) {
        const PtrWrapper<Base>& w = *it;
        w()->identify(cerr);
        cerr << " ";
    }
    cerr << endl << endl;

    return 0;
}

(derived1 101) (derived2 201) (derived2 202) (derived1 102) (derived2 203)


Checking for an item in a map
This is from a bug we found in our code a while back.
    typedef Map<int, X*, less<int> > XMap;
    XMap xmap;
    //
    // populate xmap will what-not.
    //
    const X* xx = xmap[5];
    if (xx == 0) {   // not in map.
 do_something();
    } else {
 do_something_else();
    }


looks pretty innocuous, but what really happens is that a new entry for xmap[5] is created and gets stuffed with a NULL pointer which causes amazing amount of headache 10,000 lines of code later. The right way of course (and documented in the fine manual) is the following:


    typedef Map<int, X*, less<int> > XMap;
    XMap xmap;
    //
    // populate xmap will what-not.
    //
    XMap::const_iterator it = xmap.find(5);
    if (it == xmap.end()) {  // not in map.
 do_something();
    } else {
 do_something_else();
    }



More on evils of char*: Phone-book example
Motto: Never use char* if you can help it! See the following buggy example:


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

typedef map<char*, unsigned long, less<char*> > Phonebook;

static void print_phbook(ostream& os, const Phonebook& map_) {
    for(Phonebook::const_iterator i = map_.begin(); i != map_.end(); ++i) {
        os << "\t(" << (*i).first << " " << (*i).second << ") " << endl;
    }
}

static void change_phno(
    Phonebook& phbook, const char* name, unsigned long phno
) {
    char buf[1024];
    strcpy(buf, name);
    phbook[(char*)buf] = phno;
};

int main(int, char*[]) {
    Phonebook phbook;

    phbook[(char*)"grumpy"] = 5551212;
    phbook[(char*)"sleepy"] = 5552121;
    phbook[(char*)"gandhi"] = 5554242;

    cerr << "==> Initial phone book" << endl;
    print_phbook(cerr, phbook);
    cerr << endl;

    change_phno(phbook, "grumpy", 9995152);

    cerr << "==> Grumpy moved. The new number is 9995152" << endl;
    print_phbook(cerr, phbook);
    cerr << endl;

    char buf[1024];


Page : << Previous 4  Next >>