В конце программы созданные массивы удаляются, а при вызове их деструкторов также удаляются и все их объекты. Процесс удаления отражен в строке 16 результатов выполнения программы.
При следующем выполнении программы (результаты показаны в строках 18-43) были закомментированы несколько строк программного кода (со 114 по 118), содержащие специализированный конструктор класса Array. В результате при выполнении программы для создания массива объектов Animal вызывается конструктор шаблона, показанныйвстроках74-81.
Это приводит к созданию временных объектов Animal для каждого члена массива (строки программы 79 и 80), что отражается в строках 18-20 результатов выполнения программы.
Во всех остальных аспектах, результаты выполнения двух вариантов программы, как и следовало ожидать, идентичны.
Статические члены и шаблоны
В шаблоне можно объявлять статические переменные-члены. В результате каждый экземпляр шаблона будет иметь собственный набор статических данных. Например, если добавить статическую переменную-член в шаблон Array (например, для подсчета количества созданных массивов), то в рассмотренной выше программе будут созданы две статические переменные-члена: одна для подсчета массивов объектов типа Animal и другая для массивов целых чисел. Добавление статической переменной-члена и статической функции в шаблон Array показано в листинге 19.7.
Листинг 19.7. Использование статических переменных-членов и функций-членов с шаблонам
1: #include <iostream.h>
2:
3: const int DefaultSize = 3;
4:
5: // Обычный класс, из объектов которого создается массив
6: class Animal
7: {
8: public:
9: // конструкторы
10: Animal(int);
11: Animal();
12: ~Animal();
13:
14: // методы доступа
15: int GetWeight() const { return itsWeight: }
16: void SetWeight(int theWeight) { itsWeight = theWeight }
17:
18: // дружественные операторы
19: friend ostream& operator<< (ostream&, const Animal&);
20:
21: private:
22: int itsWeight;
23: };
24:
25: // оператор вывода обьектов типа Anlmal
26: ostream& operator<<
27: (ostream& theStream, const Animal& theAnimal)
28: {
29: theStream << theAnimal.GetWeight();
30: return theStream;
31: }
32:
33: Animal::Animal(int weight):
34: itsWeight(weight)
35: {
36: //cout << "animal(int) ";
37: }
38:
39: Animal::Animal():
40: itsWeight(0)
41: {
42: // cout << "animal() ";
43: }
44:
45: Animal::~Animal()
46: {
47: // cout << "Destroyed an animal...";
48: }
49:
50: template <class T> // объявляем шаблон и параметр
51: class Array // параметризованный класс
52: {
53: public:
54: // конструкторы
55: Array(int itsSize = DefaultSize);
56: Array(const Array &rhs);
57: ~Array() { delete [] рТуре; itsNumberArrays-; }
58:
59: // операторы
60: Array& operator=(const Array&);
61: T& operator[](int offSet) { return pType[offSet]; }
62: const T& operator[](int offSet) const
63: { return pType[offSet]; }
64: // аксессоры
65: int GetSize() const { return itsSize; }
66: static int GetNumberArrays() { return itsNumberArrays; }
67:
68: // функция-друг
69: friend ostream& operator<< (ostream&, const Array<T>&); 70:
71: private:
72: T *pType;
73: int itsSize;
74: static int itsNumberArrays;
75: };
76:
77: template <class T>
78: int Array<T>::itsNumberArrays = 0;
79:
80: template <class T>
81: Array<T>::Array(int size = DefaultSize):
82: itsSize(size)
83: {
84: pType = new T[size];
85: for (int i = 0; i<size; i++)
86: pType[i] = (T)0;
87: itsNumberArrays++;
88: }
89:
90: template <class T>
91: Array<T>& Array<T>::operator=(const Array &rhs)
92: {
93: if (this == &rhs)
94: return *this;
95: delete [] pType;
96: itsSize = rhs.GetSize();
97: pType = new T[itsSize];
98: for (int i = 0; i<itsSize; i++)
99: pType[i] = rhs[i];
100: }
101:
102: template <class T>
103: Array<T>::Array(const Array &rhs)
104: {
105: itsSize = rhs.GetSize();
106: pType = new T[itsSize];
107: for (int i = 0; i<itsSize; i++)
108: pType[i] = rhs[i];
109: itsNumberArrays++;
110: }
111:
112:
113: template <class T>
114: ostream& operator<< (ostream& output, const Array<T>& theArray)
115: {
116: for (int i = 0: i<theArray.GetSize(); i++)
117: output'<< "[" << i << "] " << theArray[i] << endl;
118: return output;
119: }
120:
121:
122:
123: int main()
124: {
125:
126: cout << Array<int>::GetNumberArrays() << " integer arraysn";
127: cout << Array<Animal>::GetNumberArrays();
128: cout << " animal arraysnn";
129: Array<int> intArray;
130: Array<Animal> animalArray;
131:
132: cout << intArray.GetNumberArrays() << " integer arraysn";
133: cout << animalArray.GetNumberArrays();
134: cout << " animal arraysnn";
135:
136: Array<int> *pIntArray = new Array<int>;
137:
138: cout << Array<int>::GetNumberArrays() << " integer arraysn";
139: cout << Array<Animal>::GetNumberArrays();
140: cout << " animal arraysnn";
141:
142: delete pIntArray;
143:
144: cout << Array<int>::GetNumberArrays() << " integer arraysn";
145: cout << Array<Animal>::GetNumberArrays();
146: cout << " animal arraysnn";
147: return 0;
148: }
Результат:
0 integer arrays
0 animal arrays
1 integer arrays
1 animal arrays
2 integer arrays
1 animal arrays
1 integer arrays
1 animal arrays
Анализ: Для экономии места в листинге опущено объявление класса Animal. В класс Array добавлена статическая переменная itsNumberArrays (в строке 74), а поскольку эта перемененная объявляется в разделе закрытых членов, в строке 66 добавлен открытый статический метод доступа GetNumberArrays().
Инициализация статической переменной-члена выполняется явно в строках 77 и 78. Конструкторы и деструктор класса Array изменены таким образом, чтобы могли отслеживать число массивов, существующих в любой момент времени.
Доступ к статической переменной, заданной в шаблоне, можно получить так же, как и при работе со статическими переменными-членами обычного класса: с помощью метода доступа, вызванного для объекта класса, как показано в строках 132 и 133, или явным обращением к переменной класса, как показано в строках 126 и 127. Обратите внимание, что при обращении к статической переменной-члену необходимо указать тип массива, так как для каждого типа будет создана своя статическая переменная-член.
Рекомендуется:Используйте статические члены в шаблонах. Специализируйте выполнение шаблона путем замещения функций шаблона для разных типов. Указывайте параметр типа при вызове статических функций шаблона, чтобы получить доступ к функции требуемого типа.
Стандартная библиотека шаблонов
Отличительной чертой новой версии языка C++ является принятие стандартной библиотеки шаблонов (Standard Template Library — STL). Все основные разработчики компиляторов теперь предлагают библиотеку STL как составную часть своих программных продуктов. STL — это библиотека классов контейнеров, базирующихся на шаблонах. Она включает векторы, списки, очереди и стеки, а также ряд таких общих алгоритмов, как сортировка и поиск.
Цель включения библиотеки STL состоит в том, чтобы избавить вас от очередного изобретения колеса и при разработке выполнить за вас рутинные общепринятые процессы. Библиотека STL оттестирована и отлажена, отличается высокой эффективностью и не требует дополнительных затрат. Важнее всего то, что библиотеку STL можно использовать многократно для разработки собственных приложений. Необходимо только один раз разобраться в принципах использования библиотеки STL и классов- контейнеров.
Контейнеры
Контейнер — это объект, который содержит другие объекты. Стандартная библиотека C++ предоставляет ряд классов-контейнеров, являющихся мощными инструментальными средствами, которые помогают разработчикам C++ решать наиболее общие задачи программирования. Среди классов контейнеров стандартной библиотеки шаблонов (STL) различаются два типа: последовательные и ассоциативные. Последовательные контейнеры предназначены для обеспечения последовательного или произвольного доступа к своим членам, или элементам. Ассоциативные контейнеры оптимизированы таким образом, чтобы получать доступ к своим элементам по ключевым значениям. Подобно другим компонентам стандартной библиотеки C++, библиотека STL совместима с различными операционными системами. Все классы-контейнеры библиотеки STL определены в пространстве имен std.
Последовательные контейнеры
Такие контейнеры стандартной библиотеки шаблонов обеспечивают эффективный последовательный доступ к списку объектов. Стандартная библиотека C++ предоставляет три вида последовательных контейнеров: векторы, списки и двухсторонние очереди.