Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
B.Eckel - Thinking in C++, Vol.2, 2nd edition.pdf
Скачиваний:
50
Добавлен:
08.05.2013
Размер:
2.09 Mб
Скачать

Typedefing a typename

The typename keyword does not automatically create a typedef. A line which reads:

typename Seq::iterator It;

causes a variable to be declared of type Seq::iterator. If you mean to make a typedef, you must say:

typedef typename Seq::iterator It;

Using typename instead of class

With the introduction of the typename keyword, you now have the option of using typename instead of class in the template argument list of a template definition. This may produce code which is clearer:

//: C03:UsingTypename.cpp

// Using 'typename' in the template argument list

template<typename T> class X { };

int main() { X<int> x;

} ///:~

You’ll probably see a great deal of code which does not use typename in this fashion, since the keyword was added to the language a relatively long time after templates were introduced.

Function templates

A class template describes an infinite set of classes, and the most common place you’ll see templates is with classes. However, C++ also supports the concept of an infinite set of functions, which is sometimes useful. The syntax is virtually identical, except that you create a function instead of a class.

The clue that you should create a function template is, as you might suspect, if you find you’re creating a number of functions that look identical except that they are dealing with

different types. The classic example of a function template is a sorting function.11 However, a function template is useful in all sorts of places, as demonstrated in the first example that follows. The second example shows a function template used with containers and iterators.

11 See C++ Inside & Out (Osborne/McGraw-Hill, 1993) by the author, Chapter 10.

Chapter 15: Multiple Inheritance

124

A string conversion system

//: C03:stringConv.h

// Chuck Allison's string converter #ifndef STRINGCONV_H

#define STRINGCONV_H #include <string> #include <sstream>

template<typename T>

T fromString(const std::string& s) { std::istringstream is(s);

T t;

is >> t; return t;

}

template<typename T>

std::string toString(const T& t) { std::ostringstream s;

s << t;

return s.str();

}

#endif // STRINGCONV_H ///:~

Here’s a test program, that includes the use of the Standard Library complex number type:

//: C03:stringConvTest.cpp #include "stringConv.h" #include <iostream> #include <complex>

using namespace std;

int main() {

int i =

1234;

cout <<

"i == \"" << toString(i) << "\"\n";

float x

= 567.89;

cout <<

"x == \"" << toString(x) << "\"\n";

complex<float> c(1.0, 2.0);

cout <<

"c == \"" << toString(c) << "\"\n";

cout <<

endl;

Chapter 15: Multiple Inheritance

125

i = fromString<int>(string("1234")); cout << "i == " << i << endl;

x = fromString<float>(string("567.89")); cout << "x == " << x << endl;

c = fromString< complex<float> >(string("(1.0,2.0)")); cout << "c == " << c << endl;

} ///:~

The output is what you’d expect:

i == "1234"

x == "567.89" c == "(1,2)"

i == 1234

x == 567.89 c == (1,2)

A memory allocation system

There are a few things you can do to make the raw memory allocation routines malloc( ), calloc( ) and realloc( ) safer. The following function template produces one function getmem( ) that either allocates a new piece of memory or resizes an existing piece (like realloc( )). In addition, it zeroes only the new memory, and it checks to see that the memory is successfully allocated. Also, you only tell it the number of elements of the type you want, not the number of bytes, so the possibility of a programmer error is reduced. Here’s the header file:

//: C03:Getmem.h

// Function template for memory #ifndef GETMEM_H

#define GETMEM_H #include "../require.h" #include <cstdlib> #include <cstring>

template<class T>

void getmem(T*& oldmem, int elems) {

typedef int cntr; // Type of element counter const int csz = sizeof(cntr); // And size const int tsz = sizeof(T);

if(elems == 0) { free(&(((cntr*)oldmem)[-1]));

Chapter 15: Multiple Inheritance

126

return;

}

T* p = oldmem; cntr oldcount = 0;

if(p) { // Previously allocated memory

//Old style:

//((cntr*)p)--; // Back up by one cntr

//New style:

cntr* tmp = reinterpret_cast<cntr*>(p); p = reinterpret_cast<T*>(--tmp);

oldcount = *(cntr*)p; // Previous # elems

}

T* m = (T*)realloc(p, elems * tsz + csz); require(m != 0);

*((cntr*)m) = elems; // Keep track of count const cntr increment = elems - oldcount; if(increment > 0) {

// Starting address of data:

long startadr = (long)&(m[oldcount]); startadr += csz;

// Zero the additional new memory: memset((void*)startadr, 0, increment * tsz);

}

// Return the address beyond the count: oldmem = (T*)&(((cntr*)m)[1]);

}

template<class T>

inline void freemem(T * m) { getmem(m, 0); }

#endif // GETMEM_H ///:~

To be able to zero only the new memory, a counter indicating the number of elements allocated is attached to the beginning of each block of memory. The typedef cntr is the type of this counter; it allows you to change from int to long if you need to handle larger chunks (other issues come up when using long, however – these are seen in compiler warnings).

A pointer reference is used for the argument oldmem because the outside variable (a pointer) must be changed to point to the new block of memory. oldmem must point to zero (to allocate new memory) or to an existing block of memory that was created with getmem( ). This function assumes you’re using it properly, but for debugging you could add an additional tag next to the counter containing an identifier, and check that identifier in getmem( ) to help discover incorrect calls.

Chapter 15: Multiple Inheritance

127

If the number of elements requested is zero, the storage is freed. There’s an additional function template freemem( ) that aliases this behavior.

You’ll notice that getmem( ) is very low-level – there are lots of casts and byte manipulations. For example, the oldmem pointer doesn’t point to the true beginning of the memory block, but just past the beginning to allow for the counter. So to free( ) the memory block, getmem( ) must back up the pointer by the amount of space occupied by cntr. Because oldmem is a T*, it must first be cast to a cntr*, then indexed backwards one place. Finally the address of that location is produced for free( ) in the expression:

free(&(((cntr*)oldmem)[-1]));

Similarly, if this is previously allocated memory, getmem( ) must back up by one cntr size to get the true starting address of the memory, and then extract the previous number of elements. The true starting address is required inside realloc( ). If the storage size is being increased, then the difference between the new number of elements and the old number is used to calculate the starting address and the amount of memory to zero in memset( ). Finally, the address beyond the count is produced to assign to oldmem in the statement:

oldmem = (T*)&(((cntr*)m)[1]);

Again, because oldmem is a reference to a pointer, this has the effect of changing the outside argument passed to getmem( ).

Here’s a program to test getmem( ). It allocates storage and fills it up with values, then increases that amount of storage:

//: C03:Getmem.cpp

// Test memory function template #include "Getmem.h"

#include <iostream> using namespace std;

int main() { int* p = 0; getmem(p, 10);

for(int i = 0; i < 10; i++) { cout << p[i] << ' ';

p[i] = i;

}

cout << '\n'; getmem(p, 20);

for(int j = 0; j < 20; j++) { cout << p[j] << ' ';

p[j] = j;

}

cout << '\n';

Chapter 15: Multiple Inheritance

128

Соседние файлы в предмете Численные методы
  • #
    08.05.20133.99 Mб22A.Menezes, P.van Oorschot,S.Vanstone - HANDBOOK OF APPLIED CRYPTOGRAPHY.djvu
  • #
  • #
    08.05.20135.91 Mб24B.Eckel - Thinking in Java, 3rd edition (beta).pdf
  • #
  • #
    08.05.20136.09 Mб17D.MacKay - Information Theory, Inference, and Learning Algorithms.djvu
  • #
    08.05.20133.85 Mб15DIGITAL Visual Fortran ver.5.0 - Programmers Guide to Fortran.djvu
  • #
    08.05.20131.84 Mб12E.A.Lee, P.Varaiya - Structure and Interpretation of Signals and Systems.djvu