Бьерн Страуструп. Язык программирования С++ Второе дополненное издание



Pdf көрінісі
бет156/256
Дата11.07.2022
өлшемі2,87 Mb.
#37591
1   ...   152   153   154   155   156   157   158   159   ...   256
8.3.4 Итерация 
В классе slist_base нет функций для просмотра списка, можно только вставлять и удалять элементы. 
Однако, в нем описывается как друг класс slist_base_iter, поэтому можно определить подходящий для 
списка итератор. Вот один из возможных, заданный в том стиле, какой был показан в $$7.8: 
class slist_base_iter { 
slink* 
ce; 
//
текущий элемент 
slist_base* 
cs; 
// 
текущий список 
public: 
inline 
slist_base_iter(slist_base& 
s); 
inline 
slink* 
operator()() 
}; 
slist_base_iter::slist_base_iter(slist_base& s) 

cs = &s; 
ce = cs->last; 

slink* slist_base_iter::operator()() 
// возвращает 0, когда итерация кончается 

slink* ret = ce ? (ce=ce->next) : 0; 
if (ce == cs->last) ce = 0; 
return 
ret; 

Исходя из этих определений, легко получить итераторы для Slist и Islist. Сначала надо определить 
дружественные классы для итераторов по соответствующим контейнерным классам: 
template class Islist_iter; 
template class Islist { 


Бьерн Страуструп.
Язык программирования С++ 
 
214 
friend class Islist_iter
// 
... 
}; 
template class Slist_iter; 
template class Slist { 
friend class Slist_iter
// 
... 
}; 
Обратите внимание, что имена итераторов появляются без определения их шаблонного класса. Это 
способ определения в условиях взаимной зависимости шаблонов типа. 
Теперь можно определить сами итераторы: 
template 
class Islist_iter : private slist_base_iter { 
public: 
Islist_iter(Islist& s) : slist_base_iter(s) { } 
T* 
operator()() 
{ return (T*) slist_base_iter::operator()(); } 
}; 
template 
class Slist_iter : private slist_base_iter { 
public: 
Slist_iter(Slist& s) : slist_base_iter(s) { } 
inline 
T* 
operator()(); 
}; 
T* Slist_iter::operator()() 

return ((Tlink*) slist_base_iter::operator()())->info; 

Заметьте, что мы опять использовали прием, когда из одного базового класса строится семейство 
производных классов (а именно, шаблонный класс). Мы используем наследование, чтобы выразить 
общность классов и избежать ненужного дублирования функций. Трудно переоценить стремление 
избежать дублирования функций при реализации таких простых и часто используемых классов как 
списки и итераторы. Пользоваться этими итераторами можно так: 
void f(name* p) 

Islist 
lst1; 
Slist 
lst2; 
lst1.insert(p); 
lst2.insert(p); 
// 
... 
Islist_iter 
iter1(lst1); 
const name* p; 
while (p=iter1()) { 
list_iter 
iter2(lst1); 
const 
name* 
q; 
while 
(q=iter2()) 

if (p == q) cout << "
найден" << *p << '\n'; 



Есть несколько способов задать итератор для контейнерного класса. Разработчик программы или 
библиотеки должен выбрать один из них и придерживаться его. Приведенный способ может показаться 
слишком хитрым. В более простом варианте можно было просто переименовать operator()() как next(). В 
обоих вариантах предполагается взаимосвязь между контейнерным классом и итератором для него, так 


Бьерн Страуструп.
Язык программирования С++ 
 
215 
что можно при выполнении итератора обработать случаи, когда элементы добавляются или удаляются 
из контейнера. Этот и некоторые другие способы задания итераторов были бы невозможны, если бы 
итератор зависел от функции пользователя, в которой есть указатели на элементы из контейнера. Как 
правило, контейнер или его итераторы реализуют понятие "установить итерацию на начало" и понятие 
"текущего элемента". 
Если понятие текущего элемента предоставляет не итератор, а сам контейнер, итерация происходит в 
принудительном порядке по отношению к контейнеру аналогично тому, как поля связи принудительно 
хранятся в объектах из контейнера. Значит трудно одновременно вести две итерации для одного 
контейнера, но расходы на память и время при такой организации итерации близки к оптимальным. 
Приведем пример: 
class slist_base { 
// 
... 
slink* last; // last->next 
голова списка 
slink* current; // 
текущий элемент 
public: 
// 
... 
slink* head() { return last?last->next:0; } 
slink* current() { return current; } 
void set_current(slink* p) { current = p; } 
slink* first() { set_current(head()); return current; } 
slink* 
next(); 
slink* 
prev(); 
}; 
Подобно тому, как в целях эффективности и компактности программы можно использовать для одного 
объекта как список с принудительной связью, так и список без нее, для одного контейнера можно 
использовать принудительную и непринудительную итерацию: 
void f(Islist& ilst) 
// медленный поиск имен-дубликатов 

list_iter slow(ilst); // 
используется итератор 
name* 
p; 
while (p = slow()) { 
ilst.set_current(p); // рассчитываем на текущий элемент 
name* 
q; 
while (q = ilst.next()) 
if 
(strcmp(p->string,q->string) 
== 
0) 
cout 
<< 
"
дубликат" << p << '\n'; 


Еще один вид итераторов показан в $$8.8. 


Достарыңызбен бөлісу:
1   ...   152   153   154   155   156   157   158   159   ...   256




©emirsaba.org 2024
әкімшілігінің қараңыз

    Басты бет