Inheritance (public, private, or protected) and containment aren’t always the solution when you want to reuse code. Consider, for example, the Stack class (see Chapter 10) and the Queue class (see Chapter 12). These are examples of container classes, which are classes designed to hold other objects or data types. The Stack class from Chapter 10, for example, stores unsigned long values. You could just as easily define a stack class for storing double values or string objects. The code would be identical except for the type of object stored. However, rather than write new class declarations, it would be nice if you could define a stack in a generic (that is, type-independent) fashion and then provide a specific type as a parameter to the class. Then you could use the same generic code to produce stacks of different kinds of values. In Chapter 10, the Stack example uses typedef as a first pass at dealing with this desire. However, that approach has a couple drawbacks. First, you have to edit the header file each time you change the type. Second, you can use the technique to generate just one kind of stack per program. That is, you can’t have a typedef represent two different types simultaneously, so you can’t use the method to define a stack of ints and a stack of strings in the same program.

C++’s class templates provide a better way to generate generic class declarations. (C++ originally did not support templates, and since their introduction, templates have continued to evolve, so it is possible that your compiler, if old, may not support all the features presented here.) Templates provide parameterized types—that is, they are capable of passing a type name as an argument to a recipe for building a class or a function. By feeding the type name int to a Queue template, for example, you can get the compiler to construct a Queue class for queuing ints.

The C++ library provides several template classes. Earlier in this chapter, you worked with the valarray template class, and Chapter 4 introduced the vector and array template classes. C++’s Standard Template Library (STL), which Chapter 16 discusses in part, provides powerful and flexible template implementations of several container classes. This chapter explores designs of a more elementary nature.

Defining a Class Template

Let’s use the Stack class from Chapter 10 as a model from which to build a template. Here’s the original class declaration:

typedef unsigned long Item;

class Stack

{

private:

    enum {MAX = 10};    // constant specific to class

    Item items[MAX];    // holds stack items

    int top;            // index for top stack item

public:

    Stack();

    bool isempty() const;

    bool isfull() const;

    // push() returns false if stack already is full, true otherwise

    bool push(const Item & item);   // add item to stack

    // pop() returns false if stack already is empty, true otherwise

    bool pop(Item & item);          // pop top into item

};

The template approach will replace the Stack definition with a template definition and the Stack member functions with template member functions. As with template functions, you preface a template class with code that has the following form:

template Type>

The keyword template informs the compiler that you’re about to define a template. The part in angle brackets is analogous to an argument list to a function. You can think of the keyword class as serving as a type name for a variable that accepts a type as a value, and of Type as representing a name for this variable.

Using class here doesn’t mean that Type must be a class; it just means that Type serves as a generic type specifier for which a real type will be substituted when the template is used. Newer C++ implementations allow you to use the less confusing keyword typename instead of class in this context:

template Type>  // newer choice

You can use your choice of generic type name in the Type position; the name rules are the same as those for any other identifier. Popular choices include T and Type; in this case, we’ll use the latter. When a template is invoked, Type will be replaced with a specific type value, such as int or string. Within the template definition, you can use the generic type name to identify the type to be stored in the stack. For the Stack case, that would mean using Type wherever the old declaration formerly used the typedef identifier Item. For example,

Item items[MAX];  // holds stack items

becomes the following:

Type items[MAX];  // holds stack items

Similarly, you can replace the class methods of the original class with template member functions. Each function heading will be prefaced with the same template announcement:

template

Again, you should replace the typedef identifier Item with the generic type name Type. One more change is that you need to change the class qualifier from Stack:: to Stack::. For example,

bool Stack::push(const Item & item)

{

...

}

becomes the following:

Перейти на страницу:

Все книги серии Developer's Library

Похожие книги