Выбрать главу

int main()

{

 atype<int> intob; // массив int-значений

 atype<double> doubleob; // массив double-значений

 int i;

 cout << "Массив int-значений: ";

 for(i=0; i<SIZE; i++) intob[i] = i;

 for(i=0; i<SIZE; i++) cout << intob[i] << " ";

 cout << '\n';

 cout << "Массив double-значений: ";

 for(i=0; i<SIZE; i++) doubleob[i] = (double) i/3;

 for(i=0; i<SIZE; i++) cout << doubleob[i] << " ";

 cout << '\n';

 intob[12] = 100; // ошибка времени выполнения!

 return 0;

}

В этой программе сначала создается обобщенный тип безопасного массива, а затем демонстрируется его использование путем создания массива целых чисел и массива double-значений. Было бы неплохо, если бы вы попробовали создать массивы других типов. Как доказывает этот пример, одно из наибольших достоинств обобщенных классов состоит в том, что они позволяют только один раз написать код, отладить его, а затем применять его к данным любого типа, не переписывая его для каждого конкретного приложения.

Использование в обобщенных классах аргументов, не являющихся типами

В template-спецификации для обобщенного класса можно также задавать аргументы, не являющиеся типами. Это значит, что в шаблонной спецификации можно указывать то, что обычно принимается в качестве стандартного аргумента, например, аргумент типа int или аргумент-указатель. Синтаксис (он практически такой же, как при задании обычных параметров функции) включает определение типа и имени аргумента. Вот, например, как можно по-другому реализовать класс безопасного массива, представленного в предыдущем разделе.

// Использование в шаблоне аргументов, которые не являются типами.

#include <iostream>

#include <cstdlib>

using namespace std;

// Здесь элемент int size - это аргумент, не являющийся типом.

template <class AType, int size>

class atype {

  AType a[size]; // В аргументе size передается длина массива.

 public:

  atype() {

   register int i;

   for(i=0; i<size; i++) a[i] = i;

  }

  AType &operator[](int i);

};

// Обеспечение контроля границ для класса atype.

template <class АТуре, int size>

AType &atype<AType, size>::operator[](int i)

{

 if(i<0 || i> size-1) {

  cout << "\n Значение индекса ";

  cout << i << " за пределами границ массива.\n";

  exit(1);

 }

 return a[i];

}

int main()

{

 atype<int, 10> intob; // 10-элементный массив целых чисел

 atype<double, 15> doubleob; // 15-элементный массив double-значений

 int i;

 cout << "Массив целых чисел: ";

 for(i=0; i<10; i++) intob[i] = i;

 for(i=0; i<10; i++) cout << intob[i] << " ";

 cout << '\n';

 cout << "Массив double-значений: ";

 for(i=0; i<15; i++) doubleob[i] = (double) i/3;

 for(i=0; i<15; i++) cout << doubleob[i] << " ";

 cout << '\n';

 intob[12] = 100; // ошибка времени выполнения!

 return 0;

}

Рассмотрим внимательно template-спецификацию для класса atype. Обратите внимание на то, что аргумент size объявлен с указанием типа int. Этот параметр затем используется в теле класса atype для объявления размера массива a. Несмотря на то что в исходном коде программы член size имеет вид "переменной", его значение известно уже во время компиляции. Поэтому его можно успешно использовать для установки размера массива. Кроме того, значение "переменной" size используется для контроля выхода за границы массива в операторной функции operator[](). Обратите также внимание на то, как в функции main() создается массив целых чисел и массив значений с плавающей точкой. При этом размер каждого из них определяется вторым параметром template-спецификации.