: _current;

return next;

}

Если элемент, на который указывает _current, удален, могут возникнуть проблемы. Их преодолевают модификацией кода функций remove() и remove_front(): они должны проверять значение _current. Если он указывает на удаляемый элемент, функции изменят его так, чтобы он адресовал следующий элемент либо был равен 0, когда удаляемый элемент – последний в списке или список стал пустым. Модифицированная remove_front() выглядит так:

inline void

ilist::remove_front()

{

if ( _at_front ) {

ilist_item *ptr = _at_front;

_at_front = _at_front-next();

// _current не должен указывать на удаленный элемент

if ( _current == ptr )

_current = _at_front;

bump_down_size();

delete ptr;

}

}

Вот модифицированный фрагмент кода remove():

while ( plist ) {

if ( plist-value() == value )

{

prev-next( plist-next() );

if ( _current == plist )

_current = prev-next();

Что произойдет, если элемент будет вставлен перед тем, на который указывает _current? Значение _current не изменяется. Пользователь должен начать проход по списку с помощью вызова init_iter(), чтобы новый элемент попал в число перебираемых. При инициализации списка другим и при присваивании значение _current не копируется, а сбрасывается в 0.

Тестовая программа для проверки работы копирующего конструктора и оператора присваивания выглядит так::

#include iostream

#include "ilist.h"

int main()

{

ilist mylist;

for ( int ix = 0; ix 10; ++ix ) {

mylist.insert_front( ix );

mylist.insert_end( ix );

}

cout "\n" "Применение init_iter() и next_iter()"

"для обхода всех элементов списка:\n";

ilist_item *iter;

for ( iter = mylist.init_iter();

iter; iter = mylist.next_iter() )

cout iter-value() " ";

cout "\n" "Применение копирующего конструктора\n";

ilist mylist2( mylist );

mylist.remove_all();

for ( iter = mylist2.init_iter();

iter; iter = mylist2.next_iter() )

cout iter-value() " ";

cout "\n" "Применение копирующего оператора присваивания\n";

mylist = mylist2;

for ( iter = mylist.init_iter();

iter; iter = mylist.next_iter() )

cout iter-value() " ";

cout "\n";

}

Результат работы программы:

Применение init_iter() и next_iter() для обхода всех элементов списка:

9 8 7 6 5 4 3 2 1 0 0 1 2 3 4 5 6 7 8 9

Применение копирующего конструктора

9 8 7 6 5 4 3 2 1 0 0 1 2 3 4 5 6 7 8 9

Применение копирующего оператора присваивания

9 8 7 6 5 4 3 2 1 0 0 1 2 3 4 5 6 7 8 9

<p>5.11.1. Обобщенный список</p>

Наш класс ilist имеет серьезный недостаток: он может хранить элементы только целого типа. Если бы он мог содержать элементы любого типа – как встроенного, так и определенного пользователем, – то его область применения была бы гораздо шире. Модифицировать ilist для поддержки произвольных типов данных позволяет механизм шаблонов (см. главу 16).

При использовании шаблона вместо параметра подставляется реальный тип данных. Например:

list string slist;

создает экземпляр списка, способного содержать объекты типа string, а

list int ilist;

создает список, в точности повторяющий наш ilist. С помощью шаблона класса можно обеспечить поддержку произвольных типов данных одним экземпляром кода. Рассмотрим последовательность действий, уделив особое внимание классу list_item.

Определение шаблона класса начинается ключевым словом template, затем следует список параметров в угловых скобках. Параметр представляет собой идентификатор, перед которым стоит ключевое слово class или typename. Например:

template class elemType

class list_item;

Эта инструкция объявляет list_item шаблоном класса с единственным параметром-типом. Следующее объявление эквивалентно предыдущему:

template typename elemType

class list_item;

Перейти на страницу:

Похожие книги