- •Ооп: Лекция 6. Шаблоны. Основы обобщенного программирования.
- •Введение в шаблоны - шаблоны функций
- •Автоматический вывод типов в шаблонах
- •Шаблоны классов
- •Технические проблемы использования шаблонов
- •Константы в аргументах шаблона
- •Компоновка шаблонов
- •Пример: собственная шаблонная реализация std::initializer_list
- •Пример: шаблонная реализация связного списка
- •Обобщенные алгоритмы
- •Полные примеры из лекции
Пример: шаблонная реализация связного списка
Более сложная задача - создание шаблона для связного списка. Основное усложнение здесь вносит наличие в списке внутренних классов для узлов и итераторов. Ниже представлено объявление шаблона класса List, в основе которого пример из предыдущих лекций. Код полностью переработан под применение шаблонов, и реализация всех методов перенесена в заголовочный файл. Необходимости в list.cpp больше нет. Т.е., для компоновки будет применяться обычная модель включения, поскольку набор возможных аргументов для инстанцирования не является конечным.
В приведенном ниже фрагменте листинга изменения, связанные с шаблонами, выделены синим. Ссылка на полную версию примера приведена в конце лекции.
list.hpp
#ifndef _LIST_HPP_
#define _LIST_HPP_
/*****************************************************************************/
#include <initializer_list>
#include <utility>
#include <stdexcept>
/*****************************************************************************/
template< typename T >
class List
{
/*-----------------------------------------------------------------*/
private:
/*-----------------------------------------------------------------*/
// Внутренний узел списка
class Node
{
/*-------------------------------------------------------------*/
public:
/*-------------------------------------------------------------*/
Node ( const T & _value )
: m_value( _value )
, m_pNext( nullptr )
{}
const T & GetValue () const { return m_value; }
T & GetValue () { return m_value; }
Node const * GetNext () const { return m_pNext; }
Node * GetNext () { return m_pNext; }
void SetNext ( Node * _pNext ) { m_pNext = _pNext; }
void ClearNext () { m_pNext = nullptr; }
/*-------------------------------------------------------------*/
private:
/*-------------------------------------------------------------*/
// Хранимое значение обобщенного типа
T m_value;
Node * m_pNext;
/*-------------------------------------------------------------*/
};
/*-----------------------------------------------------------------*/
public:
/*-----------------------------------------------------------------*/
// Итератор списка
class Iterator
{
/*-------------------------------------------------------------*/
public:
/*-------------------------------------------------------------*/
explicit Iterator ( Node * _pNode = nullptr );
const T & operator * () const;
T & operator * ();
bool operator == ( Iterator i ) const;
bool operator != ( Iterator i ) const;
Iterator & operator ++ ();
Iterator operator ++ ( int );
/*-------------------------------------------------------------*/
private:
/*-------------------------------------------------------------*/
friend class List;
void validate () const;
/*-------------------------------------------------------------*/
Node * m_pCurrent;
/*-------------------------------------------------------------*/
};
/*-----------------------------------------------------------------*/
// Конструктор по умолчанию
List ();
// Конструктор по списку инициализаторов
template< typename U >
List ( std::initializer_list< U > _l );
// Конструктор копий
template< typename U >
List ( const List< U > & _l );
// Конструктор перемещения
List ( List< T > && _l );
// Деструктор
~List ();
// Оператор копирующего присвоения
template< typename U >
List< T > & operator = ( const List< U > & _l );
// Оператор перемещающего присвоения
List< T > & operator = ( List< T > && _l );
/*-----------------------------------------------------------------*/
// Методы доступа к итераторам
Iterator begin () const;
Iterator end () const;
/*-----------------------------------------------------------------*/
// Метод определения пустоты списка
bool IsEmpty () const;
// Метод вычисления количества элементов в списке
int Size () const;
// Метод очистки списка
void Clear ();
// Метод вставки нового значения в конец списка
void PushBack ( const T & _value );
// Метод вставки нового значения в начало списка
void PushFront ( const T & _value );
// Метод удаления значения с конца списка
void PopBack ();
// Метод удаления значения с начала списка
void PopFront ();
// Метод вставки значения после указанной итератором позиции
void InsertAfter ( Iterator _prevPos, const T & _value );
// Метод вставки значения перед указанной итератором позицией
void InsertBefore ( Iterator _nextPos, const T & _value );
// Метод удаления узла по указанной итератором позиции
void Delete ( Iterator _pos );
// Метод удаления узла, стоящего до указанной итератором позиции
void DeleteBefore ( Iterator _nextPos );
// Метод удаления узла, стоящего после указанной итератором позиции
void DeleteAfter ( Iterator _prevPos );
/*-----------------------------------------------------------------*/
private:
/*-----------------------------------------------------------------*/
// Метод копирования данных
void CopyDataFrom ( const List< T > & _l );
// Метод проверки принадлежности итератора списку
bool Owns ( Iterator _pos ) const;
/*-----------------------------------------------------------------*/
// Начальный и конечный узлы списка
Node * m_pFirst;
Node * m_pLast;
/*-----------------------------------------------------------------*/
};
/*****************************************************************************/
// ... реализация операций списка и его итератора ...
/*****************************************************************************/
#endif // _LIST_HPP
Среди элементов реализации интересен сложный синтаксис при работе с внутренними классами. Чтобы разобраться с данным синтаксисом, необходимо помнить, что класс-список является шаблоном, а итератор и узел внутри него - нет, что определяет необходимость в угловых скобках.
template< typename T >
void List< T >::Iterator::validate () const
{
if ( !m_pCurrent )
throw std::logic_error( "Invalid list iterator state" );
}
Еще более интересный синтаксис используется при возврате объекта-итератора. Здесь необходимо использовать вложенный в шаблон класса тип еще до того, как становится понятным класс, в состав которого входит данный метод. Чтобы помочь прочитать данное определение правильно, каждый элемент шаблона вынесен в отдельную строку:
template< typename T >
typename List< T >::Iterator & // возврат ссылки на итератор списка
List< T >::Iterator::operator ++ ()
{
validate();
m_pCurrent = m_pCurrent->GetNext();
return * this;
}
Ниже приведена тестовая программа. Здесь используется определение явного инстанцирования в тестовом файле, исключительно для целей проверки реализации всех 100% методов абстракции на нескольких типах данных. В реальной библиотеке, такой заголовочный файл просто включается в программу без явного инстанцирования.
test_list.cpp
/*****************************************************************************/
#include "list.hpp"
#include <iostream>
#include <string>
/*****************************************************************************/
// Тестовое явное инстанцирование
template class List< int >;
template class List< double >;
template class List< std::string >;
/*****************************************************************************/
// Вспомогательный шаблон функции для вывода содержимого списка на экране
template< typename T >
void printRange ( const List< T > & _l )
{
std::cout << "List(" << _l.Size() << "): ";
for ( const T & val : _l )
std::cout << val << ' ';
std::cout << std::endl;
}
/*****************************************************************************/
int main ()
{
// 3 списка с разными типами элементов
List< int > l1{ 1, 2, 3, 4, 5 };
List< double > l2{ 1.0, 2.0, 3.0, 4.0 };
List< std::string > l3{ "A", "B", "C" };
// Распечатываем содержимое на экране, используя один и тот же шаблон
printRange( l1 );
printRange( l2 );
printRange( l3 );
}
/*****************************************************************************/
