Шаблоны.
Шаблоны позволяют использовать одни и те же функции или классы для обработки данных разных типов. Концепция шаблонов может быть использована в двух видах: по отношению к функциям и по отношению к классам. Сначала мы взглянем на шаблоны функций, а затем на шаблоны классов.
Допустим, вам нужно написать функцию вычисления модуля чисел. Из школьного курса алгебры вы, конечно, знаете, что модуль — это абсолютное значение числа, то есть число без знака. Напомню, что: модуль числа 3 равен 3, модуль числа -3 так же равен 3.
Скорее всего, функция вычисления модуля будет использоваться с каким-либо одним типом данных:
int abs(int n) //вычисление модулей целых чисел
{return(n<0)? –n: n; //если n отрицательное, вернуть -n
}
Определенная выше функция берет аргумент типа int и возвращает результат того же типа. Но теперь представьте, что понадобилось найти модуль числа типа long. Придется писать еще одну функцию:
long abs(long n) //вычисление модулей чисел типаlong
{return(n<0)? –n: n; //если n отрицательное, вернуть -n
}
Да, теперь еще одну для float:
float abs(float n) //модуль целых чисел
{return(n<0)? –n: n; //если n отрицательное, вернуть -n
}
Тело функций, как видите, ничем не отличается. И всё же они совершенно разные, поскольку обрабатывают аргументы и возвращают значения разных типов. Они могут быть перегружены и иметь одинаковые имена, это правда, но все равно для каждой из них нужно писать отдельное определение. Многократное переписывание этих функций-близнецов очень утомляет, затрудняет читабельность кода, зато делает его увесистым, что не всегда хорошо. Ведь если где-то в алгоритме попадется ошибка, придется исправлять ее в теле каждой функции. Причем некорректное исправление ошибки приведет к несостоятельности программы.
Было бы неплохо, если бы был какой-то способ написать эту функцию всего один раз и заставить ее работать с различными типами данных, возвращая, соответственно, результаты разного типа. Это как раз то, для чего существуют шаблоны функций в С++. Эта простая идея схематично изображена на следующем рисунке:

много шаблонных функций в памяти
Программно такую конструкцию можно изобразить используя ключевое слово template и другое, а именно:
template <class T> //шаблон функции
Т abs(Т n)
{return(n<0)? –n: n;
}
Всё это и есть шаблонная функция.
А сейчас немного подробнее о синтаксисе.
Краеугольным камнем концепции шаблонов функции является представление использующегося функцией типа не в виде какого-либо специфического (например, int), а с помощью названия, вместо которого может быть подставлен любой тип. В приведенном примере таким именем является Т (На его месте может быть все что угодно, например anyТуре). Ключевое слово template сообщает компилятору о том, что мы определяем шаблон функции. Ключевое слово class, заключенное в угловые скобки, можно с тем же успехом заменить на tуре. Как вы уже видели, можно определять собственные типы, используя классы, поэтому разницы между типами и классами в известном смысле нет совсем. Переменная, следующая за словом class (Т в нашем примере), называется аргументом шаблона. Внутри всего определения шаблона любой конкретный тип данных, такой, как int, заменяет аргумент Т. В abs() это имя встречается лишь дважды в первой строке в качестве типа аргумента и одновременно в качестве типа функции. В более сложном случае оно могло бы встретиться много раз, в том числе в теле функции.
Рассмотрим пример с использованием шаблонных функций.
Пример 18:
#include <iostream>
using std::cout;
using std::endl;
// function template printArray definition
template< typename T >
void printArray(const T *array, int count)
{
for (int i = 0; i < count; i++)
cout << array[ i ] << " ";
cout << endl;
} // end function template printArray
int main()
{
const int aCount = 5; // size of array a
const int bCount = 7; // size of array b
const int cCount = 6; // size of array c
int a[ aCount ] = { 1, 2, 3, 4, 5 };
double b[ bCount ] = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7 };
char c[ cCount ] = "HELLO"; // 6th position for null
cout << "Array a contains:" << endl;
// call integer function-template specialization
printArray(a, aCount);
cout << "Array b contains:" << endl;
// call double function-template specialization
printArray(b, bCount);
cout << "Array c contains:" << endl;
// call character function-template specialization
printArray(c, cCount);
return 0;
} // end main
8.2 Шаблоны классов.
Шаблонный принцип можно расширить и на классы. В этом случае шаблоны обычно используются, когда класс является хранилищем данных.
Идея шаблонных классов во многом сходна с идеей шаблонных функции. Ключевые слова template и само определение класса говорят о том, что весь класс будет шаблонным. Вот например:
template <class Type>
class A
{ Данные и методы используют шаблонный аргумент Туре
};
Шаблоны классов отличаются от шаблонов функций способом реализации. Для создания шаблонной функции мы вызываем ее с аргументами нужного типа. Классы же реализуются с помощью определения объекта, использующего шаблонный аргумент:
A <float> st;
Такое выражение создаст объект st. Это какой-то объект, в котором хранятся числа типа float. Компилятор резервирует область памяти для данных этого объекта, используя тип float везде, где появляется аргумент Туре в спецификации класса. Резервируется место и под методы класса. Конечно же, методы используют только тип float.
Создание объекта типа А, хранящего объекты каких-то других типов, как в выражении
A <float> st;
означает не только резервирование другого объема памяти для данных, но и создание нового набора методов, оперирующих типом float.
Имейте и виду, что имя типа st состоит из именикласса A и шаблонного аргумента: A <float>. Это отличает его от других классов, которые могут быть созданы из того же шаблона (A <int> или A<long>).
Рассмотрим пример с использованием шаблонного класса.
Пример 20:
Файл cpp//
#include <iostream>
using std::cout;
using std::endl;
#include "Stack.h" // Stack class template definition
int main()
{
Stack< double > doubleStack(5); // size 5
double doubleValue = 1.1;
cout << "Pushing elements onto doubleStack\n";
// push 5 doubles onto doubleStack
while (doubleStack.push(doubleValue))
{
cout << doubleValue << ' ';
doubleValue += 1.1;
} // end while
cout << "\nStack is full. Cannot push " << doubleValue
<< "\n\nPopping elements from doubleStack\n";
// pop elements from doubleStack
while (doubleStack.pop(doubleValue))
cout << doubleValue << ' ';
cout << "\nStack is empty. Cannot pop\n";
Stack< int > intStack; // default size 10
int intValue = 1;
cout << "\nPushing elements onto intStack\n"; // push 10 integers onto intStack
while (intStack.push(intValue))
{
cout << intValue << ' ';
intValue++;
} // end while
cout << "\nStack is full. Cannot push " << intValue
<< "\n\nPopping elements from intStack\n";
// pop elements from intStack
while (intStack.pop(intValue))
cout << intValue << ' ';
cout << "\nStack is empty. Cannot pop" << endl;
return 0;
} // end main
//Файл hpp //
#ifndef 20_H
#define 20_H
template< typename T >
class Stack
{
public:
Stack(int = 10); // default constructor (Stack size 10)
// destructor
~Stack()
{
delete [] stackPtr; // deallocate internal space for Stack
} // end ~Stack destructor
bool push(const T&); // push an element onto the Stack
bool pop(T&); // pop an element off the Stack
// determine whether Stack is empty
bool isEmpty() const
{
return top == -1;
} // end function isEmpty
// determine whether Stack is full
bool isFull() const
{
return top == size - 1;
} // end function isFull
private:
int size; // # of elements in the stack
int top; // location of the top element (-1 means empty)
T *stackPtr; // pointer to internal representation of the Stack
}; // end class template Stack
// constructor template
template< typename T >
Stack< T >::Stack(int s)
: size(s > 0? s: 10), // validate size
top(-1), // Stack initially empty
stackPtr(new T[ size ]) // allocate memory for elements
{
// empty body
} // end Stack constructor template
// push element onto Stack;
// if successful, return true; otherwise, return false
template< typename T >
bool Stack< T >::push(const T &pushValue)
{
if (!isFull())
{
stackPtr[ ++top ] = pushValue; // place item on Stack
return true; // push successful
} // end if
return false; // push unsuccessful
} // end function template push
// pop element off Stack;
// if successful, return true; otherwise, return false
template< typename T >
bool Stack< T >::pop(T &popValue)
{
if (!isEmpty())
{
popValue = stackPtr[ top-- ]; // remove item from Stack
return true; // pop successful
} // end if
return false; // pop unsuccessful
} // end function template pop
Вывод.
В данной курсовой работе были рассмотрены следующие разделы объектно-ориентированного языка программирования С++:
-структура программы на языке С++;
-заголовочные файлы;
-потоки ввода-вывода;
-характеристики объектно-ориентированного языка.
-объекты;
-классы;
-наследование;
-пользовательские типы данных;
-полиморфизм и перегрузка;
-арифметические операции с присваиванием;
-функции;
-простые функции;
-перегруженные функции;
-встраиваемые функции;
-аргументы по умолчанию;
-области видимости и класс памяти;
-виртуальные функции;
-конструкторы;
-деструкторы;
-простой класс;
-объявления класса;
-данные классов;
-методы классов;
-объекты-парамметры;
-объекты-результаты функций;
-массивы объектов класса;
-абстрактный класс;
-друзья;
-дружественные классы;
-дружественные функции;
-шаблоны функций;
-шаблоны классов.
Рассмотрев эти разделы мы убедились, в том что объектно-ориентированный язык программирования С++ – это мощный и компактный язык программирования. Он популярен, поскольку предоставляет в распоряжение программиста полезные инструменты программирования, обеспечивает хорошее управление аппаратными средствами.






