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

Шаблоны.

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

Допустим, вам нужно написать функцию вычисления модуля чисел. Из школьного курса алгебры вы, конечно, знаете, что модуль — это абсолютное значение числа, то есть число без знака. Напомню, что: модуль числа 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


Вывод.

В данной курсовой работе были рассмотрены следующие разделы объектно-ориентированного языка программирования С++:

-структура программы на языке С++;

-заголовочные файлы;

-потоки ввода-вывода;

-характеристики объектно-ориентированного языка.

-объекты;

-классы;

-наследование;

-пользовательские типы данных;

-полиморфизм и перегрузка;

-арифметические операции;

-арифметические операции с присваиванием;

-функции;

-простые функции;

-перегруженные функции;

-встраиваемые функции;

-аргументы по умолчанию;

-области видимости и класс памяти;

-виртуальные функции;

-конструкторы;

-деструкторы;

-простой класс;

-объявления класса;

-данные классов;

-методы классов;

-объекты-парамметры;

-объекты-результаты функций;

-массивы объектов класса;

-абстрактный класс;

-друзья;

-дружественные классы;

-дружественные функции;

-шаблоны функций;

-шаблоны классов.

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




double arrow
Сейчас читают про: