Генерация конкретных функций по шаблону




ЛАБОРАТОРНАЯ РАБОТА

Тема: Статические поля данных в классах.Шаблоны функций

Цель работы: Изучение приемов работы со статическими полями данных классов и техники создания параметризованных определений функций.

Отчет: Тексты программ с необходимыми комментариями, контрольный пример и результаты выполнения программ.

Статические члены-данные класса

В С++ статическим может быть не только создаваемый в программе объект (переменная), но и отдельное поле данных в описании класса.

Если в описании класса определить поле данных с модификатором static, то этот поле будет принадлежать классу, а все объекты будут совместно использовать его.

Статическое поле данных не является частью объектов и не копируется в каждом экземпляре (не входит в размер объекта). Это напоминает глобальную в классе переменную.

Особенностью статических полей в С++ является то, что выделение памяти для статического поля данных предусматривается еще до того, как создан первый объект этого класса.

в) Использование статических членов-данных в программах

Использование статического поля предполагает два подготовительных этапа: объявления и определения.

Объявлением статического поля — это его описание в определении класса с помощью модификатора static.

Выделение памяти для статического поля и его инициализация происходит в момент его определения.

Определение статических полей похоже на определение глобальных переменных. Но от обычных глобальных переменных их отличает то, что нужно указать принадлежность переменной конкретному классу. Это выполняется с помощью оператора разрешения области видимости (scope resolution).

Статические члены-данные подчиняются обычным для классов правам доступа. Если статическое поле объявлено как private, то доступ к нему разрешен как ко всякому private-полю только из методов класса. Однако инициализация скрытых статических данных (private) также производится на глобальном уровне и тем же способом, что и общедоступных (public) статических членов-данных.

Оба этапа создания статического поля иллюстрирует следующий пример:

class Test1{

static int var; //объявление статического private-поля

};

int Test1::var=10; // определение (создание) статического поля,

// выполняется вне класса, на глобальном уровне

Test1 x; // теперь можно использовать класс без проблем

 

Если инициализатор (в примере это 10) не указать, статическое поле по умолчанию инициализируется нулем.

Допускается инициализация одного статического поля класса значением другого, ранее определенного статического поля:

int Test::i=5;

int Test::j=Test::i; // даже если i описано как private!

 

Если статическое поле объявлено как общедоступное (public), то к нему можно обращаться тремя способами, причем два из них обычные, а третий — специфический:

1) Test a;

a.j=8; //статическое поле принадлежит каждому объекту

2) Test *pa=&a;

pa->j=9; //доступ через указатель на объект

3) Test::j=7; //к статическому полю можно обращаться

// непосредственно, через имя класса

ЗАДАНИЕ 1 (применение статических членов-данных)

1. Определить класс Test, в котором имеется статическое поле данных count типа int для подсчета количества созданных экземпляров класса (объектов). При определении этого статического поля оно должно быть обнулено.

Кроме того, в классе имеется обычное поле number типа int, в которое при создании объекта будет помещен порядковый номер объекта.

Оба поля описываются в разделе private.

В классе определен конструктор без параметров, в котором

а) значение счетчика count увеличивается на единицу и выводится на экран с новой строки в виде constr=…,

б) значение счетчика запоминается в поле number.

В классе определен public-метод int get_number(), возвращающий порядковый номер объекта.

2. Написать программу, использующую класс Test. Что нужно обязательно сделать, чтобы можно было пользоваться этим классом?

В функции main создается объект типа Test, а затем с помощью операции sizeof выводится его размер. Пояснить результат.

3. Добавить в программу обычную функцию

void print_number(Test t),

которая через аргумент получает объект типа Test, и выводит его порядковый номер в виде print = ….

4. Проверить использование статического поля данных. В main добавить определение еще двух объектов типа Test. Вызвать функцию print_number, передав ей последний созданный объект.

Шаблоны функций

Понятие шаблона функции

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

Например, есть функция swap, меняющая местами значения своих аргументов, имеющих тип int (объяснить, как работает функция):

void swap(int &x, int &y){

int z;

z=x; x=y; y=z;

}

Но в программе может потребоваться также функция, меняющая местами значения аргументов long, float и других. Все различия между этими вариантами функции будут заключаться в замене типов данных в списке аргументов и в определении вспомогательной локальной переменной z.

Чтобы избежать в подобных задачах многократного дублирования кода, С++ позволяет создавать шаблоны (или параметризованные) функций.

Можно описать сразу семейство однотипных функций, у которых типы некоторых данных описаны как "переменные" (заключено в кавычки, потому что это не идентификаторы, а специальная конструкция).

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

2. Объявления и описания шаблонов функций

Работа с шаблонными функциями подразумевает два основных этапа: описание шаблона и генерацию конкретных функций.

Объявление или описание шаблона функции

а) начинается с ключевого слова template (англ. шаблон);

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

Описание каждого параметра настройки шаблона использует ключевое слово class, которое говорит о том, что в описании функции параметризуется тип.

Пример.

Два варианта описания параметризованной функции swap:

template <class T> void swap(T &x, T &y){ T z; z=x; x=y; y=z; } template <class T> void swap(T *x, T *y) { T z; z=*x; *x=*y; *y=z; }

Шаблон функции — это объявление семейства функций. Шаблон служит схемой (скелетом) для порождения множества экземпляров функций. Встретив в программе описание шаблона, компилятор запоминает только структуру описанной в нем функции.

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

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

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

//прототип

template <class T> T f(T x);

//описание функции

template <class U> U f(U x)

{...}

Однако объявление шаблона (прототип) может быть только глобальным, то есть, размещено обязательно на внешнем уровне и не допускается внутри функции. Некоторые особенности шаблонных функций описаны в Приложении А.

Генерация конкретных функций по шаблону

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

Процесс построения конкретной функции на основании шаблона называют конкретизацией.

Конкретизация функции-шаблона компилятором выполняется в двух случаях:

а) если компилятор встретил в программе вызов функции,

б) если компилятор встретил в программе операцию взятия адреса функции.

Например, если определен шаблон функции f

template <class T> T f(T x,T y){ return x+y;},

конкретизация будет производиться в каждом из следующих случаев вызова функции f:

int i=2, j=3, k=f(i,j);

//по типам i и j генерируется функция с аргументами int

long (*pfl)(long,long)=f;

// описан указатель на функцию с двумя аргументами типа long,

// для его инициализации нужна функция f с аргументами типа long

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

Это соответствие устанавливается сопоставлением формальных параметров шаблона, используемых в описании аргументов функции, с типами фактических параметров вызова функции.

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

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

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

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

ЗАДАНИЕ 2 (создание и использование шаблонной функции)

Функция sum_array используется для подсчета суммы элементов массивов разных числовых типов (int, float, double). Она имеет два аргумента. Через первый ей передается массив, а через второй — количество элементов в этом массиве.

Функция возвращает сумму чисел в виде значения того же типа, что и элементы массива.

1. Написать шаблонную функцию sum_array.

2. В функции main определить массивы типа long и double с разным числом элементов. С помощью функции sum_array подсчитать и вывести на экран суммы их элементов



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2016-04-27 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: