Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ООП_ Лекция №06 - Шаблоны. Основы обобщенного программирования..docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
138.46 Кб
Скачать

Пример: шаблонная реализация связного списка

Более сложная задача - создание шаблона для связного списка. Основное усложнение здесь вносит наличие в списке внутренних классов для узлов и итераторов. Ниже представлено объявление шаблона класса 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 );

}

/*****************************************************************************/