Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
c++book1.doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
1.83 Mб
Скачать

5.3.2 Законченный Класс

Программирование без сокрытия данных (с применением структур) требует меньшей продуманности, чем программирование со сокрытием данных (с использованием классов). Структуру можно определить не слишком задумываясь о том, как ее предпо­лагается использовать. А когда определяется класс, все внима­ние сосредотачивается на обеспечении нового типа полным мно­жеством операций; это важное смещение акцента. Время, потраченное на разработку нового типа, обычно многократно окупается при разработке и тестировании программы.

Вот пример законченного типа intset, который реализует понятие "множество целых":

class intset (*

int cursize, maxsize;

int *x;

public:

intset(int m, int n); // самое большее, m int'ов в 1..n

~intset();

int member(int t); // является ли t элементом?

void insert(int t); // добавить "t" в множество

void iterate(int& i) (* i = 0; *)

int ok(int& i) (* return i<cursize; *)

int next(int& i) (* return x[i++]; *)

*);

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

#include <stream.h>

void error(char* s)

(*

cerr << "set: " << s << "\n";

exit(1);

*)

Класс intset используется в main(), которая предполагает два целых параметра. Первый параметр задает число случайных чисел, которые нужно сгенерировать. Второй параметр указывает диапазон, в котором должны лежать случайные целые:

main(int argc, char* argv[])

(*

if (argc != 3) error("ожидается два параметра");

int count = 0;

int m = atoi(argv[1]); // число элементов множества

int n = atoi(argv[2]); // в диапазоне 1..n

intset s(m,n);

while (count<m) (*

int t = randint(n);

if (s.member(t)==0) (*

s.insert(t);

count++;

*)

*)

print_in_order(&s);

*)

В программе, для которой требуется два параметра, счет­чик числа параметров, argc, должен равняться трем, потому что имя программы всегда передается как argv[0]. Функция

extern int atoi(char*);

функция atoi() это стандартная библиотечная функция для преобразования представления целого в виде строки в его внут­реннюю (двоичную) форму. Случайные числа генерируются с по­мощью стандартной функции rand():

extern int rand(); // Не очень случайные, будьте осторожны

int randint(int u) // в диапазоне 1..u

(*

int r = rand();

if (r < 0) r = -r;

return 1 + r%u ;

*)

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

intset::intset(int m, int n)//самое большее,m int'ов в 1..n

(*

if (m<1 !! n<m) error("недопустимый размер intset");

cursize = 0;

maxsize = m;

x = new int[maxsize];

*)

intset::~intset()

(*

delete x;

*)

Целые числа вставляются, поэтому они хранятся в возрас­тающем порядке:

void intset::insert(int t)

(*

if (++cursize > maxsize) error("слишком много элементов");

int i = cursize-1;

x[i] = t;

while (i>0 && x[i-1]>x[i]) (*

int t = x[i]; // переставить x[i] и [i-1]

x[i] = x[i-1];

x[i-1] = t;

i--;

*)

*)

Для нахождения членов используется просто двоичный по­иск:

int intset::member(int t) // двоичный поиск

(*

int l = 0;

int u = cursize-1;

while (l <= u) (*

int m = (l+u)/2;

if (t < x[m])

u = m-1;

else if (t > x[m])

l = m+1;

else

return 1; // найдено

*)

return 0; // не найдено

*)

И, наконец, нам нужно обеспечить множество операций, чтобы пользователь мог осуществлять цикл по множеству в неко­тором порядке, поскольку представление intset от пользователя скрыто. Множество внутренней упорядоченности не имеет, поэто­му мы не можем просто дать возможность обращаться к вектору (завтра я, наверное, реализую intset по-другому, в виде свя­занного списка).

Дается три функции: iterate() для инициализации итера­ции, ok() для проверки, есть ли следующий элемент, и next() для того, чтобы взять следующий элемент:

class intset (*

// ...

void iterate(int& i) (* i = 0; *)

int ok(int& i) (* return i<cursize; *)

int next(int& i) (* return x[i++]; *)

*);

Чтобы дать возможность этим трем операциям работать сов­местно и чтобы запомнить, куда дошел цикл, пользователь дол­жен дать целый параметр. Поскольку элементы хранятся в отсор­тированном списке, их реализация тривиальна. Теперь можно определить функцию печати по порядку print_in_order:

void print_in_order(intset* set)

(*

int var;

set->iterate(var);

while (set->ok(var)) cout << set->next(var) << "\n";

*)

Другой способ задать итератор приводится в #6.8.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]