Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции / LECS15.DOC
Скачиваний:
43
Добавлен:
16.04.2013
Размер:
114.18 Кб
Скачать

В файле string8.H

class string_iterator {

public:

string_ierator (my_string& s) : cur_ind (0), ptr_s (&s) { }

bool successor ( );

char& item ( ) { return ((ptr_s -> st -> s) [cur_ind]); }

void reset (int n = 0) { cur_ind = n; }

int position ( ) { return cur_ind; }

private:

my_string* ptr_s;

int cur_ind;

};

bool string_iterator :: successor ( )

{

if (cur_ind >= ptr_s -> st -> len –1)

return false;

else {

++ cur_ind;

return true;

}

}

Используем итератор для поиска в строке следующего слова.

В файле string8.Cpp

void word (string_iterator& it_s, char* w)

{

while (isspace (it_s. item ( ) ) && it_s.successor ( ) )

; //ищем не-пробел

if (!isspace (it_s. item ( ) ) ) {

*w = it_s. item ( ); //первый символ в слове

while (it_s. successor ( ) && !isspace (it_s. item ( ) ) )

*++w = it_s. item ( ); //последующие символы

}

*++w = 0; //добавляем ‘\0’ (терминатор)

}

Эта процедура пропускает символы, для которых isspace ( ) принимает истинное значение. Затем она собирает слово как набор не- isspace ( ) символов и завершает его нулевым символом.

Обобщенное программирование с использованием void*

Тип указателя void* служит в качестве обобщенного или универсального указателя. Ему разрешается присваивать указатели любых других типов. Поэтому void* можно использовать полиморфно, разрабатывая код, косвенно управляющий объектами любого типа. Изложенное можно продемонстрировать на примере определения стандартной функции копирования памяти memcpy.

В файле memcopy.cpp

#include <stddef.h> //определяем size_t

#include <iostream.h>

void* memcpy (void* to, const void* from, size_t n_bytes)

{

const char* f = reinterpret_cast<const char*>(from);

char t = reinterpret_cast<char>(to);

for (int I – 0; i < n_bytes; ++I)

t [i] = f[i];

return to;

}

int main ( )

{

char v [4];

int w = 0x00424344;

memcpy (v, &w, 4); //полиморфный интерфейс

cout << w << “ ==” << v;

}

Функция memcpy допускает аргументы-указатели любого типа. Она используется для побайтового копирования символов начиная с адреса, заданного from. В приведенном фрагменте мы инициализировали четырехбайтовый символьный массив целым значением, хранящимся в w. кроме того, эта техника может использоваться с контейнерными классами, такими как stack, для того чтобы они могли (косвенно) хранить произвольные значения.

В файле genstack.H

//реализация обобщенного стека: genstack.h

typedef void* generic_ptr;

class stack {

public:

explicit stack (int size = 1000) : max_len (size), top (EMPTY)

{ s = new generic_ptr [size]; assert (s != 0); }

~stack ( ) { delete [ ] s ; }

void reset ( ) { top = EMPTY; }

void push (generic_ptr c) { s[++top] = c; }

generic_ptr pop ( ) { return s[top--]; }

generic_ptr top_of ( ) { return s[top]; }

bool empty ( ) const { return top == EMPTY; }

bool full ( ) const { return top == max_len – 1; }

private:

enum { EMPTY = -1};

generic_ptr* s;

int max_len;

int top;

};

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

В файле month.cpp

#include <iostream.h>

#include “genstack.h”

char* months [12] = { “январь”, “февраль”, “март”, “апрель”, “май”, “июнь”,

“июль”, “август”, “сентябрь”, “октябрь”, “ноябрь”, “декабрь”};

int main ( )

{

stack a;

int i;

for (i = 0; i<12; ++i)

  1. push (months [i]);

for (i = 0; i<12; ++i)

cout << reinterpret_cast<char*> (a.pop( ) )

<< end1;

}

Написание обобщенной процедуры обращения порядка следования значений, использующей стек, мы оставим до упражнения 10 не стр. 242.

Список и итератор списка

В этом разделе мы разработаем двусвязный список, интерфейс которого близок к библиотечному типу list из STL. Двусвязный список жертвует размером в пользу эффективности. Добавляя связь, которая явно указывает на предыдущий элемент списка, мы упрощаем такие операции, как удаление; правда, за это приходится платить хранением дополнительных указателей для всех элементов списка. Рассмотрим модель такого класса и связанных с ним определений итератора.

В файле list2.h

class list {

public:

struct listelem; //предварительные объявления

class iterator;

friend iterator;

list ( ) : h (0), t(0) { } //создаем пустой список

~list ( ) { release ( ) ; }

iterator begin ( ) const {return h; }

iterator end ( ) const {return t; }

void push_front (char c);

void pop_front ( );

char& front ( ) {return h -> data;}

char& back ( ) {return t -> data;}

bool empty ( ) const { return h == 0; }

void release ( );

private:

listelem* h, *t; //голова и хвост списка

struct listelem //ячейка списка

{

char data;

listelem* next, *prev;

listelem (char c, listelem* n, listelem* p)

:data ©, next (n), prev (p) { }

};

}

Исследуем схему этого класса поэтапно, начиная со скрытой реализации. Имеется структура listelem, которая содержит член данных списка и два указателя: на следующий и предыдущий элементы. Сам список представлен в виде указателей на голову и хвост. Проход по элементам списка легко выполнить как в прямом направлении – начиная с h с помощью члена next, так и в обратном направлении – начиная с t – с помощью члена prev. Такой проход останавливается, когда значение указателя становится равным 0.

Мы добавили класс-итератор, вложив его внутрь класса списка. Этот класс

list :: iterator будет использоваться для указания на текущую позицию внутри списка. Он сродни указателю-курсору. Таким образом, мы должны также уметь перемещаться по списку с помощью операторов автоинкремента и автодекремента, определенных в классе итератора. Это можно сделать следующим образом.

Соседние файлы в папке Лекции