containers and templates





[31] Container classes and templates, C++ FAQ Lite







[31] Container classes and templates
(Part of C++ FAQ Lite, Copyright © 1991-98, Marshall Cline, cline@parashift.com)

FAQs in section [31]:

[31.1] How can I make a perl-like associative array in
C++?
[31.2] How can I build a <favorite container> of objects of
different types?
[31.3] How can I insert/access/change elements from a linked
list/hashtable/etc?
[31.4] What's the idea behind templates?
[31.5] What's the syntax / semantics for a "function template"?
[31.6] What's the syntax / semantics for a "class template"?
[31.7] What is a "parameterized type"?
[31.8] What is "genericity"?



[31.1] How can I make a perl-like associative array in
C++?
Use the standard class template map<Key,Val>:

    #include <string>
    #include <map>
    #include <iostream>
    using namespace std;
    
    main()
    {
      map<string,int,less<string> >  age;   // age is a map from string to int
    
      age["Fred"] = 42;                     // Fred is 42 years old
      age["Barney"] = 37;                   // Barney is 37
    
      if (todayIsFredsBirthday())           // On Fred's birthday,
        ++ age["Fred"];                     // increment Fred's age
    
      cout << "Fred is " << age["Fred"] << " years old\n";
    }

[ Top | Bottom | Previous section | Next section ]


[31.2] How can I build a <favorite container> of objects of
different types?
You can't, but you can fake it pretty well. In C/C++ all arrays are
homogeneous (i.e., the elements are all the same type). However, with an extra
layer of indirection you can give the appearance of a heterogeneous container
(a heterogeneous container is a container where the contained objects are of
different types).
There are two cases with heterogeneous containers.
The first case occurs when all objects you want to store in a container are
publicly derived from a common base class. You can then declare/define your
container to hold pointers to the base class. You indirectly store a derived
class object in a container by storing the object's address as an element in
the container. You can then access objects in the container indirectly through
the pointers (enjoying polymorphic behavior). If you need to know the exact
type of the object in the container you can use dynamic_cast<> or
typeid(). You'll probably need the Virtual Constructor
Idiom to copy a container of disparate object types. The
downside of this approach is that it makes memory management a little more
problematic (who "owns" the pointed-to objects? if you delete these
pointed-to objects when you destroy the container, how can you guarantee that
no one else has a copy of one of these pointers? if you don't delete these
pointed-to objects when you destroy the container, how can you be sure that
someone else will eventually do the deleteing?). It also makes copying the
container more complex (may actually break the container's copying functions
since you don't want to copy the pointers, at least not when the container
"owns" the pointed-to objects).
The second case occurs when the object types are disjoint — they do not share
a common base class. The approach here is to use a handle class. The
container is a container of handle objects (by value or by pointer, your
choice; by value is easier). Each handle object knows how to "hold on to"
(i.e. ,maintain a pointer to) one of the objects you want to put in the
container. You can use either a single handle class with several different
types of pointers as instance data, or a hierarchy of handle classes that
shadow the various types you wish to contain (requires the container be of
handle base class pointers). The downside of this approach is that it opens up
the handle class(es) to maintenance every time you change the set of types
that can be contained. The benefit is that you can use the handle class(es)
to encapsulate most of the ugliness of memory management and object lifetime.
Thus using handle objects may be beneficial even in the first case.
[ Top | Bottom | Previous section | Next section ]


[31.3] How can I insert/access/change elements from a linked
list/hashtable/etc?
I'll use an "inserting into a linked list" as a prototypical example. It's
easy to allow insertion at the head and tail of the list, but limiting
ourselves to these would produce a library that is too weak (a weak library is
almost worse than no library).
This answer will be a lot to swallow for novice C++'ers, so I'll give a couple
of options. The first option is easiest; the second and third are better.

Empower the List with a "current location," and member functions
such as advance(), backup(), atEnd(), atBegin(), getCurrElem(),
setCurrElem(Elem), insertElem(Elem), and removeElem(). Although this
works in small examples, the notion of a current position makes it
difficult to access elements at two or more positions within the list (e.g.,
"for all pairs x,y do the following...").
Remove the above member functions from List itself, and move them
to a separate class, ListPosition. ListPosition would act as a "current
position" within a list. This allows multiple positions within the same list.
ListPosition would be a friend of class List, so
List can hide its innards from the outside world (else the innards of List
would have to be publicized via public member functions in List). Note:
ListPosition can use operator overloading for things like advance() and
backup(), since operator overloading is syntactic sugar for normal member
functions.
Consider the entire iteration as an atomic event, and create a
class template to embodies this event. This enhances performance by
allowing the public access member functions (which may be
virtual functions) to be avoided during the
inner loop. Unfortunately you get extra object code in the application, since
templates gain speed by duplicating code. For more, see [Koenig, "Templates
as interfaces," JOOP, 4, 5 (Sept 91)], and [Stroustrup, "The C++ Programming
Language Third Edition," under "Comparator"].

[ Top | Bottom | Previous section | Next section ]


[31.4] What's the idea behind templates?
A template is a cookie-cutter that specifies how to cut cookies that all look
pretty much the same (although the cookies can be made of various kinds of
dough, they'll all have the same basic shape). In the same way, a class
template is a cookie cutter for a description of how to build a family of
classes that all look basically the same, and a function template describes
how to build a family of similar looking functions.
Class templates are often used to build type safe containers (although this
only scratches the surface for how they can be used).
[ Top | Bottom | Previous section | Next section ]


[31.5] What's the syntax / semantics for a "function template"?
Consider this function that swaps its two integer arguments:

    void swap(int& x, int& y)
    {
      int tmp = x;
      x = y;
      y = tmp;
    }

If we also had to swap floats, longs, Strings, Sets, and FileSystems, we'd get
pretty tired of coding lines that look almost identical except for the type.
Mindless repetition is an ideal job for a computer, hence a function
template:

    template<class T>
    void swap(T& x, T& y)
    {
      T tmp = x;
      x = y;
      y = tmp;
    }

Every time we used swap() with a given pair of types, the compiler will go to
the above definition and will create yet another "template function" as an
instantiation of the above. E.g.,

    main()
    {
      int    i,j;  /*...*/  swap(i,j);  // Instantiates a swap for int
      float  a,b;  /*...*/  swap(a,b);  // Instantiates a swap for float
      char   c,d;  /*...*/  swap(c,d);  // Instantiates a swap for char
      String s,t;  /*...*/  swap(s,t);  // Instantiates a swap for String
    }

Note: A "template function" is the instantiation of a "function template".
[ Top | Bottom | Previous section | Next section ]


[31.6] What's the syntax / semantics for a "class template"?
Consider a container class Array that acts like an array of integers:

    // This would go into a header file such as "Array.h"
    class Array {
    public:
      Array(int len=10)                  : len_(len), data_(new int[len]) { }
     ~Array()                            { delete [] data_; }
      int len() const                    { return len_;     }
      const int& operator[](int i) const { return data_[check(i)]; }
            int& operator[](int i)       { return data_[check(i)]; }
      Array(const Array&);
      Array& operator= (const Array&);
    private:
      int  len_;
      int* data_;
      int  check(int i) const
        { if (i < 0 || i >= len_) throw BoundsViol("Array", i, len_);
          return i; }
    };

Just as with swap() above, repeating the above over and over for Array of
float, of char, of String, of Array-of-String, etc, will become tedious.

    // This would go into a header file such as "Array.h"
    template<class T>
    class Array {
    public:
      Array(int len=10)                : len_(len), data_(new T[len]) { }
     ~Array()                          { delete [] data_; }
      int len() const                  { return len_;     }
      const T& operator[](int i) const { return data_[check(i)]; }
            T& operator[](int i)       { return data_[check(i)]; }
      Array(const Array<T>&);
      Array<T>& operator= (const Array<T>&);
    private:
      int len_;
      T*  data_;
      int check(int i) const
        { if (i < 0 || i >= len_) throw BoundsViol("Array", i, len_);
          return i; }
    };

Unlike template functions, template classes (instantiations of class
templates) need to be explicit about the parameters over which they are
instantiating:

    main()
    {
      Array<int>           ai;
      Array<float>         af;
      Array<char*>         ac;
      Array<String>        as;
      Array< Array<int> >  aai;
    }

Note the space between the two >'s in the last example. Without this
space, the compiler would see a >> (right-shift) token instead of two
>'s.
[ Top | Bottom | Previous section | Next section ]


[31.7] What is a "parameterized type"?
Another way to say, "class templates."
A parameterized type is a type that is parameterized over another type or some
value. List<int> is a type (List) parameterized over another type (int).
[ Top | Bottom | Previous section | Next section ]


[31.8] What is "genericity"?
Yet another way to say, "class templates."
Not to be confused with "generality" (which just means avoiding solutions which
are overly specific), "genericity" means class templates.
[ Top | Bottom | Previous section | Next section ]


 E-mail the author
[ C++ FAQ Lite
| Table of contents
| Subject index
| About the author
| ©
| Download your own copy ]
Revised May 27, 1998




Wyszukiwarka

Podobne podstrony:
Outlook Stationery Letterheads And Templates Using Signatures
2005 02 Ready for Press Templates and Pdfs in Scribus
Reaction and Strength of a Fiberglass Container under Internal Explosive Loading
Cards and Counters Template
EV (Electric Vehicle) and Hybrid Drive Systems
Madonna Goodnight And Thank You
Found And Downloaded by Amigo
2002 09 Creating Virtual Worlds with Pov Ray and the Right Front End
Functional Origins of Religious Concepts Ontological and Strategic Selection in Evolved Minds
Found And Downloaded by Amigo
Beyerl P The Symbols And Magick of Tarot
find?tors and use?sesCB35F0
Found And Downloaded by Amigo

więcej podobnych podstron