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

Примеры алгоритмов и итераторов

Представим алгоритм копирования последовательности. Такой алгоритм должен принимать интервал входных итераторов копируемой последовательности, и выходной итератор для результирующей последовательности.

template< typename _InputIt, typename _OutputIt >

OutputIt MyCopy ( InputIt _first, InputIt _last, OutputIt _outFirst )

{

InputIt current = _first;

OutputIt outputCurrent = _outFirst;

while ( current != _last )

{

* outputCurrent = * inputCurrent;

++ inputCurrent;

++ outputCurrent;

}

return _outputCurrent;

}

Отметим, что такой подход не вставляет новых элементов в целевую последовательность, а лишь перезаписывает существующие. Необходимо заранее позаботиться о соответствии между длиной копируемой последовательности и размером целевого контейнера:

int data[ 5 ] = { 1, 2, 3, 4, 5 };

std::list< int > myList( 5 ); // создаем список из 5 нулевых элементов

MyCopy( data, data + 5, myList.begin() ); // перезаписывает нулевые значения

Если же требуется обеспечить вставку новых элементов, т.е. дописывание в конец контейнера, имеет смысл реализация специального вида Output-итератора, автоматически вставляющего элемент. Подобное средство std::back_insert_iterator определено в стандартной библиотеке приблизительно следующим образом:

// Специальный итератор вставки:

// _Container - тип контейнера

template< typename _Container >

class back_insert_iterator

{

// Вспомогательный синоним, обозначающий такой же тип

typedef back_insert_iterator< _Container > Self;

// Вспомогательный синоним, обозначающий тип данных в контейнере

typedef typename _Container::value_type ValueType;

// Ссылка на целевой контейнер с правом на запись

Container & m_container;

public:

// Конструктор: принимает ссылку на целевой контейнер с правом на запись

explicit back_insert_iterator ( Container & _c )

: m_container( _c )

{}

// Оператор сравнения на равенство

bool operator == ( const Self & _it ) const

{

return & m_container == & _it.m_container ;

}

// Оператор сравнения на неравенство

bool operator != ( const Self & _it ) const

{

return !( * this == _it );

}

// Оператор разыменования с правом на запись -

// возвращает ссылку на себя, т.к. итератор можно присваивать

Self & operator * ()

{

return * this;

}

// Оператор копирующего присвоения значения - вставляет элемент в контейнер

Self & operator = ( const ValueType & _v )

{

m_container.push_back( _v );

return * this;

}

// Оператор префиксного инкремента (вызов игнонируется)

Self & operator ++ ()

{

return * this;

}

// Оператор постфиксного инкремента (вызов игнонируется)

Self operator ++ ( int )

{

return * this;

}

};

// Вспомогательная функция для создания back_insert_iterator без указания типов

template< typename _T, typename _Container >

back_insert_iterator< _T, _Container > back_inserter ( _Container & _c )

{

return back_insert_iterator< _T, _Container >*( _c );

}

// Копирование в изначально пустой список со вставкой в конец

int data[ 5 ] = { 1, 2, 3, 4, 5 };

std::list< int > myList;

MyCopy( data, data + 5, back_inserter( _c ) );

Реализация использует синоним типа value_type, который определен в каждом STL-контейнере. Декларирование синонима повышает читабельность кода при использовании в методах:

// Вспомогательный синоним, обозначающий тип данных в контейнере

typedef typename _Container::value_type ValueType;

Аналогичным образом определяются output-итераторы, вставляющие значения в начало последовательности при помощи метода push_front (std::front_inserter) и в произвольную позицию в контейнере, указываемую при создании итератора (std::inserter).

Форвардные итераторы потребуются для алгоритма, который заменяет некоторое искомое значение в последовательности на новое. В таком алгоритме требуется одновременно и читать, и модифицировать последовательность:

template< typename ForwardIt, typename T >

void MyReplace (

ForwardIt _first

, ForwardIt _last

, const T & _oldValue

, const T & _newValue

)

{

ForwardIt current = _first;

while ( current != _last )

{

if ( * current == _ oldValue )

* current = _newValue;

++ current;

}

}

Примером необходимости в двунаправленных итераторах является алгоритм, зеркально переворачивающий порядок значений в последовательности:

template< typename _BidIt >

void MyReverse ( _BidIt _first, _BidIt _last )

{

while ( _first != _last && _first != -- _last )

{

std::swap( * _first, * _last );

++ _first;

}

}

Наконец, ярким примером алгоритмов, ожидающих итераторов произвольного доступа, является семейство алгоритмов сортировки:

template< typename _RanIt >

void MyBubbleSort ( _RanIt _first, _RanIt _last )

{

int nElements = _last - _first;

for ( int i = 0; i < nElements - 1; i++ )

for ( int j = nElements - 1; j > i; j-- )

if ( *( _first + j - 1 ) > *( _first + j ) )

std::swap( * ( _first + j - 1 ), * ( _first + j ) );

}