Параметризованные типы и функции. (2 час.)

Ofstream object (filename, flag)

где flag может иметь следующие значения:

ios::app запись в конец существующего файла

ios::ate после открытия файла перейти в его конец

ios::binary открыть файл в двоичном режиме (по умолчанию - текстовый)

ios::in открыть для чтения

ios::nocreate сообщать о невозможности открытия, если файл не существует

ios::noreplace сообщать о невозможности открытия, если файл существует

ios::out открыть для вывода

ios::trunc если файл существует, стереть содержимое.

При необходимости изменить способ открытия или применения файла можно при создании файлового потока использовать два флага или более флага: ios::app|ios::noreplace

Для открытия файла одновременно на чтение и запись можно использовать объекты класса fstream:

fstream object(filename, ios::in|ios::app);

Перед завершением программа проверяет, находятся ли потоки в приемлемом состоянии. При завершении программы открытые файлы неявно закрываются. Для явного закрытия объектов файловых потоков применяется метод close():

object.close();

Ввод-вывод в файлы.Для ввода/вывода в потоковые объекты можно применять методы put(), get(), для связывания объекта с различными файлами служат методы open(), close(), для позиционирования в файле имеются методы seekg(), seekp(), tellp(). При этом seekg() назначает или возвращает текущую позицию указателя чтения, а seekp() назначает или возвращает текущую позицию указателя записи. Обе функции могут иметь один или два аргумента. При вызове с одним аргументом функции перемещают указатель на заданное место, а при вызове с двумя аргументами вычисляется относительная позиция от начала файла (ios::beg), текущей позиции (ios::cur) или от конца файла (ios::end). Текущая позиция определяется методом tellp().

Для объектов файловых потоков контроль состояния также производится с помощью методов, манипулирующих флагами ошибок:

bad() возвращает ненулевое значение, если обнаружена ошибка

clear() сбрасывает сообщения об ошибках

eof() возвращает ненулевое значение, если обнаружен конец файла

fail() возвращает ненулевое значение, если операция завершилась неудачно

good() возвращает ненулевое значение, если флаги ошибок не выставлены

rdstate() возвращает текущее состояние флагов ошибок.

Если флаги показывают наличие ошибки, все попытки поместить в поток новые объекты будут игнорироваться, то есть состояние потока не изменится.

Шаблоны функций. Параметры шаблонов. Шаблоны классов. Наследование и шаблоны.

Шаблоны функций.Шаблоны, которые называют иногда родовыми или параметризоавнными типами, позволяют создавать (конструировать) семейства родственных функций и классов.

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

В определении шаблона семейства функций используется служебное слово template. Для параметризации используется список формальных параметров шаблона, который заключает­ся в угловые скобки <>. Каждый формальный параметр шаблона обозначается служебным словом class, за которым следует имя параметра (идентификатор). Пример определения шаблона функций, вычисляющих абсолютные значения числовых величин разных типов:

template <class type>

type abs(type x) { return x > 0 ? x: -x; };

Шаблон семейства функций состоит из двух частей - заголовка шаблона:

template <список_параметров_шаблона>

и из обыкновенного определения функции, в котором тип возвращаемого значения и типы любых параметров обозначаются именами параметров шаблона, введенных в его заголовке. Те же имена параметров шаблона могут использоваться и в теле определения функции для обозначения типов локальных объектов.

В качестве еще одного примера рассмотрим шаблон семейства функций для обмена значений двух передаваемых им параметров.

template <class T>

void swap (T* x, T* y)

{

T z = *x;

*x = *y;

*y = x;

};

Здесь параметр T шаблона функций используется не только в заголовке для спецификации формальных параметров, но и в теле определения функции, где он задает тип вспомогатель­ной переменной z.

Шаблон семейства функций служит для автоматического формирования конкретных определений функций по тем вызовам, которые транслятор обнаруживает в теле программы. Например, если программист употребляет обращение abs(-10.3),то на основе приведенного ранее шаблона компилятор сформирует такое определение функции:

double abs(double x) {return x > 0 ? x: -x; };

Далее будет организовано выполнение именно этой функции и в точку вызова в качестве результата вернется числовое значение 10.3.

Если в программе присутствует приведенный ранее шаблон семейства функций swap() и появится последовательность операторов:

long k = 4, d = 8, swap (&k, &d);

то компилятор сформирует определение функции:

void swap(long* x, long* y)

{

long x = *x;

*x = *y;

*y = x;

};

Затем будет выполнено обращение именно к этой функции и значения переменных k, d поменяются местами.

Если в той же программе присутствуют операторы:

double a = 2.44, b = 66.3; swap (&a, &b);

то сформируется и выполнится функция

void swap(double* x, double* y)

{

double x = *x;

*x = *y;

*y = x;

};

Проиллюстрируем сказанное о шаблонах на более конкретном примере. Рассмотрим программу, используем некоторые возможности функций, возвращающих значение типа «ссылка». Но тип ссылки будет определяться параметром шаблона:

#include <iostream.h>

//Функция определяет ссылку на элемент с максимальным значением

template <class type>

type& rmax(int n, type d[])

{

int im = 0;

for (int i = 1; i < n; i++)

im = d[im] > d[i] ? im : i;

return d[im];

};

void main()

{

int n = 4;

int x[] = { 10, 20, 30, 14}; //Массив целых чисел

cout << " rmax(n,x) = " << rmax (n,x); // rmax(n,x) = 30

rmax(n,x) = 0;

for (int i = 0; i < n; i++)

cout << " x[" << i << "] =" << x[i]; // x[0] = 10 x[1] ...

float arx[] = { 10.3, 20.4, 10.5}; //Массив вещественных чисел

cout << " rmax(3,arx) = " << rmax (3,arx); //rmax(3,arx) = 20.4

rmax(3, arx) = 0;

for (int i = 0; i < 3; i++)

cout << " arx[" << i << "] =" << arx[i]; //arx[0] = 10.3 ...

};

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

По существу механизм шаблонов функций позволяет автоматизировать подготовку переопределений перегруженных функций. При использовании шаблонов уже нет необходимости готовить заранее все варианты функций с перегруженным именем. Компилятор, автоматически анализируя вызовы функций в тексте программы, формирует необходимые определения именно для таких типов параметров, которые использованы в обращениях. Дальнейшая обработка выполняется так же, как и для перегруженных функций.

Параметры шаблонов.Можно считать, что параметры шаблона являются его формаль­ными параметрами, а типы тех параметров, которые используются в конкретных обращениях к функции, служат фактическими параметрами шаблона. Именно по ним выполняется пара­метрическая настройка и с учетом этих типов генерируется конкретный текст определения функции. Однако, говоря о шаблоне семейства функций, обычно употребляют термин «список параметров шаблона», не добавляя определения «формальных».

Перечислим основные свойства параметров шаблона:

1. Имена параметров шаблона должны быть уникальными во всем определении шаблона.

2. Список параметров шаблона функции не может быть пустым, так как при этом теряется возможность параметризации и шаблон функций становится обычным определением конкретной функции.

3. В списке параметров шаблона функций может быть несколько параметров. Каждый из них должен начинаться со служебного слова class. Например, допустим такой заголовок шаблона:

template <class type1, class type2>

Соответственно, неверен заголовок:

template <class type1, class type2>

4. Недопустимо использовать в заголовке шаблона параметры с одинаковыми именами, то есть такой заголовок ошибочен:

5. Имя параметра шаблона (в примерах - type1, type2) имеет в определяемой шаблоном функции все права имени типа, то есть с его помощью могут специализироваться формальные параметры, определяться тип возвращаемого функцией значения и типы любых объектов, локализованных в теле функции. Имя параметра шаблона видно во всем определении и скрывает другие использования того же идентификатора в области, глобальной по отношению к данному шаблону функций. Если внутри тела определяемой функции необходим доступ к внешним объектам с тем же именем, нужно применять операцию изменения области видимости. Следующая программа иллюстрирует указанную особенность имени параметра шаблона функций:

#include <iostream.h>

int N = 0; //статическая, инициализирована нулем

template <class N>