: _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
5.11.1. Обобщенный список
Наш класс 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;