►Зачем нужны шаблоны классов...314
"Неужели я не могу просто создать класс Array? — скажете вы. — Зачем мне возиться с шаблонами?"
Конечно, можете. Если заранее знаете, объекты какого типа будут храниться в этом массиве. Например, если вам нужен только массив целых чисел, то нет смысла ломать голову над шаблоном Vector< T > — проще создать класс IntArray и использовать его.
По сути единственной альтернативой шаблонам является использование void*, указателя, который может указывать на объекты любого типа. Этот способ использован в следующей программе.
/* VoidVector — реализация вектора с использованием */
/* void* для хранения элементов */
#include <cstdlib>
#include <cstdio>
#include <iostream>
using namespace std ;
typedef void* VoidPtr ;
class VoidVector
{
public:
VoidVector( int nArraySize )
{
/* Количество элементов */
/* Количество элементов */
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 ;
_________________
314 стр. Часть 5. Полезные особенности
}
}
VoidPtr get( ){ return ptr[ nReadIndex++ ] ; }
protected :
int nSize ;
int nWriteIndex ;
int nReadIndex ;
VoidPtr* ptr ;
} ;
int main( int argc , char* pArgs[ ] )
{
setlocale ( LC_ALL , ".1251" ) ; /* печать русских текстов */
/* Создание вектора */
VoidVector vv( 10 ) ;
/* Добавление значений к вектору */
cout << "Введите последовательность целых чисел "
"для внесения в вектор ( отрицательное "
"число завершает ввод последовательности )"
<< endl ;
for( ; ; )
{
int* p = new int ;
cin >> *p ;
if ( *p < 0 )
{
delete p ;
break ;
}
vv.add( ( void* ) p ) ;
}
cout << " Вы ввели следующие числа" << endl ;
for ( int i = 0 ; i < vv.size( ) ; i++ )
{
int* p = ( int* )vv.get( ) ;
cout << i << ":" << *p << endl ;
}
/* Пауза для того, чтобы посмотреть на результат работы программы */
system( "PAUSE" ) ; return 0 ;
}
В этой программе тип VoidPtr определён как синоним void*.
«Ключевое слово typedef создаёт новое имя для существующего типа. Вы можете везде, где видите VoidPtr, в уме вставлять void*. Использование таких замен делает текст более удобочитаемым, а также упрощает синтаксис выражений. Иногда оказывается невозможным заставить работать существующий шаблон класса с указателем, и тогда использование typedef для замены составного типа наподобие указателя может решить проблему.»
[Советы]
_________________
315 стр. Глава 27. Шаблоны С++
Класс VoidVector предоставляет те же функции-члены add( ) и get( ), что и TemplateVector из предыдущей программы.
Это решение имеет ( как минимум ) три проблемы. Во-первых, оно неудобно в использовании, как видно из текста функции main( ) — вы не в состоянии сохранить значение, и должны использовать только указатели на объекты. Это означает, что вы должны выделить для значения память в куче и поместить в вектор её адрес.
Во-вторых, если вдруг вы попытаетесь добавлять целые значения в вектор следующим образом:
int n ;
сin >> n ;
vv.add( ( void* ) &n ) ;
то у вас ничего не получится. Переменная n имеет локальную область видимости, так что при выходе из цикла for он просто потеряет всякий смысл.
«На самом деле всё ещё хуже — адрес n остаётся неизменным во всех итерациях цикла for.»
[Советы]
В-третьих, самая серьёзная проблема в том, что при получении значений из VoidVector вы должны знать их тип. С++ не может проверить тип объекта, чтобы убедиться, что ваши предположения верны. Допустим, вы решили, что в векторе хранятся не целые, а действительные числа, и использовали следующий код:
double dValue = *( double* )get( ) ;
Такая программа не будет работать корректно, поскольку в dValue в результате окажется какой-то мусор. Однако компиляция этой программы пройдёт без ошибок. Приведение типа к void* сводит на нет преимущества строгой типизации С++.
Больше книг — больше знаний!
Заберите 20% скидку на все книги Литрес с нашим промокодом
ПОЛУЧИТЬ СКИДКУ