Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ For Dummies (2004) [eng].pdf
Скачиваний:
84
Добавлен:
16.08.2013
Размер:
8.09 Mб
Скачать

358 Part V: Optional Features

Notice that the TemplateVector handles both int values and Name objects with equal ease. Notice also how similar the nameFn() and intFn() functions are, even though integers and names have nothing to do with each other.

A sample session appears as follows:

Give me a series integer values to add to a vector (Enter a negative number to terminate):

5

10

15 -1

Here are the numbers you entered 0:5 1:10 2:15

Enter names

(Enter an ‘x’ to quit): Chester

Fox

Penny x

Here are the names you entered 0:Chester

1:Fox

2:Penny

Press any key to continue . . .

Do I Really Need Template Classes?

“But,” you say, “can’t I just create a simple Array class? Why mess with templates?”

Sure you can, if you know a priori what types of things you need arrays for. For example, if all you ever need is arrays of integers, you have no reason to create a template Vector<T>; you could just create the class IntArray and be finished.

The only other alternative is to use void*, which can point to any type of object. The following VoidVector program is based upon the use of void pointers:

//VoidVector - implement a vector that relies on void*

//as the storage element

#include <cstdlib> #include <cstdio> #include <iostream>

Chapter 27: Tempting C++ Templates 359

using namespace std;

typedef void* VoidPtr;

class VoidVector

{

public:

VoidVector(int nArraySize)

{

// store off the number of elements nSize = nArraySize;

ptr = new VoidPtr[nArraySize]; reset();

}

int size() { return nWriteIndex; }

void reset() { nWriteIndex = 0; nReadIndex = 0; } void add(void* pValue)

{

if (nWriteIndex < nSize)

{

ptr[nWriteIndex++] = pValue;

}

}

VoidPtr get(){ return ptr[nReadIndex++]; }

protected: int nSize;

int nWriteIndex; int nReadIndex; VoidPtr* ptr;

};

int main(int argc, char* pArgs[])

{

//create a vector VoidVector vv(10);

//add values to the vector

cout << “Give me a series integer values to add to a vector\n”

<< “(Enter a negative number to terminate):” << endl;

for(;;)

{

int* p = new int; cin >> *p;

if (*p < 0)

{

delete p; break;

}

360 Part V: Optional Features

vv.add((void*)p);

}

cout << “\nHere are the numbers you entered” << endl; for(int i = 0; i < vv.size(); i++)

{

int* p = (int*)vv.get();

cout << i << “:” << *p << endl;

}

system(“PAUSE”); return 0;

}

This program defines a type VoidPtr to be equivalent to a void*.

The typedef keyword does nothing more than create a new name for an existing class. You can mentally insert void* everywhere you see VoidPtr. typedefs can make reading a function easier, and they also improve the syntax of a statement. It is sometimes not possible to get an existing template class to work properly when the type is a pointer. Wrapping a complex type like a pointer in a typedef solves the problem.

VoidVector provides the same add() and get() methods provided by the

TemplateVector template class in the previous program.

This solution has (at least) three problems. First, it is somewhat clumsy to use, as demonstrated in main(). It is not possible to store a value, such as 10; you can pass only the address of an object. This means that you must allocate an int* off the heap to use as a storage place for the value read from the keyboard.

The second problem is the serious opportunity for screw up. You may have been tempted to simply add an int to the collection as follows:

int n; cin >> n;

vv.add((void*)&n);

That would not work. The variable n has local scope. Its address will “go away” once control exits the for loop. At that point, the addresses in the vector will make no sense.

Actually, the problem is slightly worse — the address of n is the same for every iteration through the for loop.

The third problem is more serious. In order to retrieve a value from a

VoidVector, you must know the type of object stored there. C++ cannot

Chapter 27: Tempting C++ Templates 361

check the types to make sure that your assumption is correct. Suppose, for example, you thought that double variables were stored in vv instead of int values. The following code would have stored garbage into dValue:

double dValue = *(double*)get();

The program would certainly go astray; the casts to and from void* defeat the strong typing built into C++.

Tips for Using Templates

You should remember a few things when using templates. First, no code is gen­ erated for a template. (Code is generated after the template is converted into a concrete class or function.) This implies that a .cpp source file is almost never associated with a template class. The entire template class definition, including all the member functions, is contained in the include file so that it can be available for the compiler to expand.

Second, a template class does not consume memory. Therefore, there is no penalty for creating template classes if they are never instanced. On the other hand, a template class uses memory every time it is instanced. Thus, the code for Array<Student> consumes memory even if Array<int> already exists.

Finally, a template class cannot be compiled and checked for errors until it is converted into a real class. Thus, a program that references the template class Array<T> might compile even though Array<T> contains obvious syntax errors. The errors won’t appear until a class such as Array<int> or

Array<Student> is created.

362 Part V: Optional Features

Chapter 28

Standardizing on the Standard Template Library

In This Chapter

Using the string class

Maintaining entries in a Standard Template Library list

Accessing container elements from an iterator

Using a map container

Some programs can deal with data as it arrives and dispense with it. Most programs, however, must store data for later processing. A structure that is used to store data is known generically as a container or a collection (I use

the terms interchangeably). This book has relied heavily on the array for data storage so far. The array container has a couple of very nice properties: It stores and retrieves things very quickly. In addition, the array can be declared to hold any type of object in a type-safe way. Weighed against that, however, are two very large negatives.

First, you must know the size of the array at the time it is created. This require­ ment is generally not achievable, although you will sometimes know that the number of elements cannot exceed some “large value.” Viruses, however, com­ monly exploit this type of “it can’t be larger than this” assumption, which turns out to be incorrect. There is no real way to “grow” an array except to declare a new array and copy the contents of the old array into the newer, larger version.

Second, inserting elements anywhere within the array involves copying ele­ ments within the array. This is costly in terms of both memory and comput­ ing time. Sorting the elements within an array is even more expensive.

C++ now comes with the Standard Template Library or STL, which includes many different types of containers, each with its own set of advantages (and disadvantages).

The C++ Standard Template Library is a very large library of sometimescomplex containers. This session is considered just an overview of the power of the STL.