В двух предыдущих статьях я обсуждал, как создавать и использовать шаблоны функций в C ++. В этой и следующей статьях я расскажу, как создавать и использовать шаблоны классов. Шаблоны позволяют нам создавать общие программы, которые могут работать с различными типами данных без необходимости перегружать и / или копировать программный код. Обобщенное программирование лежит в основе стандартной библиотеки шаблонов (STL) C ++, и понимание того, как работать с шаблонами функций и классов, является основополагающим для эффективной работы с STL.
Определение шаблона класса - класс стека
Перефразируя покойного программиста Сеймура Паперта, трудно говорить о программировании, не запрограммировав что-либо. Для моих примеров шаблонов классов я сначала определю Stack
класс, а затем Pair
класс, который имитирует pair
структуру C ++.
Как многие из вас знают, стек - это структура данных, в которой данные вводятся в верхнюю часть стека (с помощью функции-члена push
), а данные удаляются только с вершины стека (с помощью функции-члена pop
). Единственная другая необходимая первичная функция-член - это функция top
, которая проверяет и возвращает элемент данных в верхней части стека.
Стеки могут использоваться со всеми типами данных, и эта возможность поддается универсальной реализации. Нам может понадобиться стек для целых чисел, или стек для строк, или стек для значений с плавающей запятой. Создавать отдельную реализацию для каждого типа данных нецелесообразно, поэтому необходимо определение шаблона.
Я начну определение класса с демонстрации того, как вы определяете класс как шаблон класса. Это очень похоже на то, как вы сначала определяете шаблон функции. Вот как я запускаю класс Stack
:
template <typename T> class Stack { // declarations and definitions };
С этого момента, когда нам нужно обратиться к типу данных, который либо ссылается на данные, поступающие в класс, либо на данные, отправляемые из класса, мы будем вводить данные с помощью заполнителя T
. Вот полное объявление класса Stack
:
template <typename T> class Stack { private: vector<T> data; public: void push(T element); bool pop(); T top(); };
Вы можете видеть, что вектор, который будет содержать данные стека, имеет тип T
, как и параметр функции-члена push
и тип возвращаемого значения функции-члена top
. Вы также должны заметить, что я не определял функцию-конструктор для класса или какие-либо служебные функции ради экономии места, а не потому, что я не думаю, что они необходимы для полного определения класса стека.
Теперь давайте посмотрим на определение класса Stack
:
template <typename T> void Stack<T>::push(T const& element) { data.push_back(element); } template <typename T> bool Stack<T>::pop() { if (!data.empty()) { data.pop_back(); return true; } return false; } template <typename T> T const& Stack<T>::top() { return data.back(); }
Обратите особое внимание на то, где используется заполнитель параметра шаблона в разделе определения. Его следует помещать в скобки каждый раз, когда упоминается имя класса, и всякий раз, когда он должен использоваться в качестве заполнителя для типа параметра или типа возвращаемого значения.
Вот программа, которая использует класс Stack
с целыми числами:
int main() { Stack<int> numbers; numbers.push(1); numbers.push(2); cout << "The top of the stack" " << numbers.top() << endl; numbers.push(3); cout << "The top of the stack: " << numbers.top() << endl; numbers.pop(); cout << "The top of the stack: " << numbers.top() << endl; return 0; }
Обратите внимание: если вы не укажете аргумент шаблона, программа не скомпилируется. В отличие от шаблонов функций, где компилятор может вывести тип данных из аргументов, с шаблонами классов компилятор не может вывести тип для класса, поскольку нет аргументов для вывода.
Второй пример: парный класс
Pair
- это класс, содержащий два элемента данных. Эти элементы данных могут быть любого типа и могут быть разных типов. Мое определение будет имитировать структуру pair
, найденную в STL и используемую с контейнерами STL, такими как карта.
Двумя переменными-членами класса являются first
и second
. Чтобы более точно имитировать версию STL, они будут объявлены в разделе public
, чтобы к ним можно было получить доступ непосредственно из объекта pair
. (Я мог бы также реализовать этот класс как структуру из-за открытого доступа к переменным-членам.)
Поскольку это простой класс, объявление также является определением для Pair
class:
template <typename T1, typename T2> class Pair { public: T1 first; T2 second; };
В структуре pair
есть функция, которая помогает создавать пары объектов, make_pair
. Вот определение моей версии makePair
:
template <typename T1, typename T2>| Pair<T1, T2> makePair(T1 first, T2 second) { Pair<T1, T2> p; p.first = first; p.second = second; return p; }
Это должен быть шаблон функции, чтобы мы могли при необходимости изменять типы для первого и второго.
Вот один пример программы, которая использует класс Pair
и функцию makePair
:
template <typename T1, typename T2> class Pair { public: T1 first; T2 second; }; template <typename T1, typename T2> Pair<T1, T2> makePair(T1 first, T2 second) { Pair<T1, T2> p; p.first = first; p.second = second; return p; } int main() { Pair<string, int> nameAge; string name = "Mary"; int age = 23; nameAge = makePair<string, int>(name, age); cout << "Name: " << nameAge.first << endl; cout << "Age: " << nameAge.second << endl; return 0; }
Вот результат этой программы:
Name: Mary Age: 23
Чтобы продемонстрировать, почему шаблоны классов полезны, вот еще одна программа, которая использует этот класс и функцию:
int main() { Pair<double, string> ratios; double r = 3.14159; string name = "pi"; ratios = makePair(r, name); cout << "Value: " << ratios.first << endl; cout << "Name: " << ratios.second << endl; return 0; }
Вот результат этой программы:
Value: 3.14159 Name: pi
Ценность шаблонов классов
Как и шаблоны функций, шаблоны классов делают возможным универсальное программирование на C ++. Универсальные программы более гибкие, чем другие парадигмы программирования, потому что, как показывает Стандартная библиотека шаблонов, мы можем смешивать и сопоставлять контейнеры (структуры данных) и алгоритмы (функции) различными способами, что невозможно в других парадигмах.
Есть другие темы, касающиеся шаблонов курсов, и я расскажу о некоторых из них в своей следующей статье.
Спасибо за чтение, напишите мне с комментариями и предложениями.