Курсовая: Шаблон иерархической структуры данных в памяти

          Министерство общего и профессионального образования РФ          
          Новосибирский государственный технический университет          
     
Кафедра ВТ Курсовая работа По дисциплине: лПрограммирование. Раздел 2 Ц Объектно-ориентированное программирование на С++ Шаблон иерархической структуры данных в памяти Факультет: АВТ Группа: АМ-311 Студент: Шевченко Н.С. Преподаватель: Новицкая Ю. В. Вариант: 3 Новосибирск, 2004
  1. Техническое задание.
Для заданной двухуровневой структуры данных, содержащей указатели на объекты (или сами объекты) - параметры шаблона, разработать полный набор операций (добавление, включение и извлечение по логическому номеру, сортировка, включение с сохранением порядка, загрузка и сохранение объектов в бинарном файле, балансировка Ц выравнивание размерностей структур данных нижнего уровня). Предполагается, что операции сравнения хранимых объектов переопределены стандартным образом (в виде операций <,> и т.д.). Проверить шаблон на простых типах и сложных типах. В качестве сложного типа можно использовать объекты- строки. Программа должна реализовывать указанные выше действия. Протестировать шаблон. Программа тестирования должна содержать меню, обеспечивающее выбор операций. Шаблон структуры данных Ц односвязный список, содержащий статический массив указателей на объекты. Последовательность указателей в каждом массиве ограничена NULL. При переполнении текущего массива указателей создается новый элемент списка, в который переписывается половина указателей из текущего. 2. Назначение и область применения программного продукта. Назначение программного продукта заключается в том, что при его разработке и реализации студент, закрепляет свои знания полученные за семестр. 3. Выбор и обоснования выбора инструментальных средств. Разработчик посчитал наиболее удобным способом реализации задания Ц использование вложенных классов. В результате можно создать наиболее универсальный шаблон. 4. Теоретический материал. Наследование - это процесс, посредством которого один объект может приобретать свойства другого. Точнее, объект может наследовать основные свойства другого объекта и добавлять к ним черты, характерные только для него. Наследование является важным, поскольку оно позволяет поддерживать концепцию иерархии классов (hierarchical classification). Применение иерархии классов делает управляемыми большие потоки информации. Виртуальные функции. Виртуальные функции преодолевают сложности решения с помощью полей типа, позволяя программисту описывать в базовом классе функции, которые можно переопределять в любом производном классе. Компилятор и загрузчик обеспечивают правильное соответствие между объектами и применяемыми к ним функциями. Шаблоны. В языке C++ возможно и параметрическое программирование (программирование с использованием родовых компонентов). Родовые (параметризованные) компоненты обладают свойством адаптивности к конкретной ситуации, в которой такой компонент используется, что позволяет разрабатывать достаточно универсальные и в то же время высокоэффективные компоненты программ (в частности, объекты). Параметрическое программирование в языке C++ реализовано с помощью шаблонов (template). В C++ определено два вида шаблонов: шаблоны-классы и шаблоны-функции. Шаблоны-классы могут использоваться многими способами, но наиболее очевидным является их использование в качестве адаптивных объектов памяти. Шаблоны-функции могут использоваться для определения параметризованных алгоритмов. Основное отличие шаблона-функции от шаблона-класса в том, что не нужно сообщать компилятору, к каким типам параметров применяется функция, он сам может определить это по типам ее формальных параметров. Шаблоны, которые называют иногда родовыми или параметризованными типами, позволяют создавать (конструировать) семейства родственных функций и классов. Цель введения шаблонов функций - автоматизация создания функций, которые могут обрабатывать разнотипные данные. В отличие от механизма перегрузки, когда для каждого набора формальных параметров определяется своя функция, шаблон семейства функций определяется один раз, но это определение параметризуется. Параметризовать в шаблоне функций можно тип возвращаемого функцией значения и типы любых параметров, количество и порядок размещения которых должны быть фиксированы. Для параметризации используется список параметров шаблона. Можно считать, что параметры шаблона являются его формальными параметрами, а типы тех параметров, которые используются в конкретных обращениях к функции, служат фактическими параметрами шаблона. Именно по ним выполняется параметрическая настройка и с учетом этих типов генерируется конкретный текст определения функции. Однако, говоря о шаблоне семейства функций, обычно употребляют термин лсписок параметров шаблона, не добавляя определения лформальных. Перечислим основные свойства параметров шаблона: 1. Имена параметров шаблона должны быть уникальными во всем определении шаблона. 2. Список параметров шаблона функции не может быть пустым, так как при этом теряется возможность параметризации и шаблон функций становится обычным определением конкретной функции. 3. В списке параметров шаблона функций может быть несколько параметров. Каждый из них должен начинаться со служебного слова class. Шаблон семейства классов определяет способ построения отдельных классов подобно тому, как класс определяет правила построения и формат отдельных объектов. В определении класса, входящего в шаблон, особую роль играет имя класса. Оно является не именем отдельного класса, а параметризованным именем семейства классов. Как и для шаблонов функций, определение шаблона класса может быть только глобальным. Обработка исключительных ситуаций: обработка ошибок в стандартном С. Еще в стандартном Си (ANSI C) имелись средства обнаружения и перехвата различных ошибок, возникающих в процессе выполнения программы, а также других исключительных ситуаций, не приводящих к завершению программы, но не позволяющих ей функционировать должным образом. К таким ситуациям относятся выходы значений переменных из допустимых диапазонов, ошибки в математических операциях и функциях, невозможность выделения требуемого объема динамической памяти. Ошибки, возникшие в процессе выполнения программы, связаны с неверной логикой ее функционирования и не могут быть выявлены на этапах компиляции и линковки, а их возникновение приводит к прекращению работы программы без указания конкретного места возникновения ошибки. В языке Си++ практически любое состояние, достигнутое в процессе выполнения программы, можно заранее определить как особую ситуацию (исключение) и предусмотреть действия, которые нужно выполнить при ее возникновении. Для реализации механизма обработки исключений в язык Си++ введены следующие три ключевых (служебных) слова: try (контролировать), catch (ловить), throw (генерировать, порождать, бросать, посылать, формировать). Служебное слово try позволяет выделить в любом месте исполняемого текста программы так называемый контролируемый блок: try {операторы} Среди операторов, заключенных в фигурные скобки могут быть: описания, определения, обычные операторы языка Си++ и специальные операторы генерации (порождения, формирования) исключений: throw выражение_генерации_исключения; Когда выполняется такой оператор, то с помощью выражения, использованного после служебного слова throw, формируется специальный объект, называемый исключением. Исключение создается как статический объект, тип которого определяется типом значения выражения_генерации_исключения. После формирования исключения исполняемый оператор throw автоматически передает управление (и само исключение как объект) непосредственно за пределы контролируемого блока. В этом месте (за закрывающейся фигурной скобкой) обязательно находятся один или несколько обработчиков исключений, каждый из которых идентифицируется служебным словом catch и имеет в общем случае следующий формат: catch (тип_исключения имя) { операторы } Об операторах в фигурных скобках говорят как о блоке обработчика исключений. Обработчик исключений (процедура обработки исключений) внешне и по смыслу похож на определение функции с одним параметром, не возвращающей никакого значения. Когда обработчиков несколько, они должны отличаться друг от друга типами исключений. Все это очень похоже на перегрузку функций, когда несколько одноименных функций отличаются спецификациями параметров. Так как исключение передается как объект определенного типа, то именно этот тип позволяет выбрать из нескольких обработчиков соответствующий посланному исключению. Механизм обработки исключений является весьма общим средством управления программой. Он может использоваться не только при обработке аварийных ситуаций, но и любых других состояний в программе, которые почему-либо выделил программист. Для этого достаточно, чтобы та часть программы, где планируется возникновение исключений, была оформлена в виде контролируемого блока, в котором выполнялись бы операторы генерации исключений при обнаружении заранее запланированных ситуаций. Переопределение операций: язык C++ не обеспечивает средств для ввода/вывода. Ему это и не нужно; такие средства легко и элегантно можно создать с помощью самого языка. Описанная здесь стандартная библиотека потокового ввода/вывода обеспечивает гибкий и эффективный с гарантией типа метод обработки символьного ввода целых чисел, чисел с плавающей точкой и символьных строк, а также простую модель ее расширения для обработки типов, определяемых пользователем. Разработка и реализация стандартных средств ввода/вывода для языка программирования зарекомендовала себя как заведомо трудная работа. Традиционно средства ввода/вывода разрабатывались исключительно для небольшого числа встроенных типов данных. Однако в C++ программах обычно используется много типов, определенных пользователем, и нужно обрабатывать ввод и вывод также и значений этих типов. Очевидно, средство ввода/вывода должно быть простым, удобным, надежным в употреблении, эффективным и гибким, и ко всему прочему полным. Ничье решение еще не смогло угодить всем, поэтому у пользователя должна быть возможность задавать альтернативные средства ввода/вывода и расширять стандартные средства ввода/вывода применительно к требованиям приложения. C++ разработан так, чтобы у пользователя была возможность определять новые типы столь же эффективные и удобные, сколь и встроенные типы. Поэтому обоснованным является требование того, что средства ввода/вывода для C++ должны обеспечиваться в C++ с применением только тех средств, которые доступны каждому программисту. Описываемые здесь средства ввода/вывода представляют собой попытку ответить на этот вызов. Средства ввода/вывода связаны исключительно с обработкой преобразования типизированных объектов в последовательности символов и обратно. Есть и другие схемы ввода/вывода, но эта является основополагающей в системе UNIX, и большая часть видов бинарного ввода/вывода обрабатывается через рассмотрение символа просто как набор бит, при этом его общепринятая связь с алфавитом игнорируется. Тогда для программиста ключевая проблема заключается в задании соответствия между типизированным объектом и принципиально нетипизированной строкой. Обработка и встроенных и определенных пользователем типов однородным образом и с гарантией типа достигается с помощью одного перегруженного имени функции для набора функций вывода. 5. Структурное описание разработки. В программе описано 3 класса. Реализация списка осуществлена при помощи шаблонного класса List и вложенного класса Elem. Сложный тип данных типа лстрока описан в классе Digit. Класс Elem является элементом списка. Он содержит массив указателей на элементы типа T (размерность массива (N) передается как параметр при определении списка), численное значение int size, которое показывает, на сколько заполнен массив указателей и указатель на следующий элемент списка Elem *next. Класс List содержит указатели на первый и последний элементы списка ( Elem *first; Elem *last;). В данном классе описаны все функции, работающие непосредственно со списком такие как: вставка в список, балансировка, сортировка, сохранение/загрузка из файла, извлечение из списка, информация о текущем состоянии списка, вывод всего списка на экран и удаление списка.
  1. Иерархия классов.

class List

{

int kol;

public:

class Elem {

public:

int size;

T * data;

Elem *next;

}

Elem *first;

Elem *last;

void info ();

void add (T);

void split (Elem*);

void ShowDataLog (int);

long count (Elem*);

void delDataLog (int);

void delList ();

void insOrd (T);

void insLog (T, int);

void sort ();

void save_bin (char*);

void load_bin (char*);

void balance ();

};

class Digit

{

char *str;

public:

int operator==(const int);

int operator<(Digit& s);

int operator<=(Digit& s);

Digit operator=(Digit& s);

void operator=(const int);

ostream& operator << (ostream& s, Digit s);

void List<Digit,8>::save_bin();

void List<Digit,8>::load_bin();

};

last

first

kol

Графическое изображение иерархии, реализованной в программе:
class List

SIZE

class Elem
class Elem
class Elem
7. Описание программы. В начале работы программы на экран выводится меню управления, которое позволяет получить доступ к следующим операциям: Рассмотрим каждую операцию отдельно: Просмотр списка Функция выводит в удобном виде содержимое списка, а именно размер массива указателей из текущего элемента списка, содержимое массива указателей, логические номера содержимого и набор допустимых операций: Поговорим о них поподробнее: 1. Добавление: Программа считывает введённую информацию и вызывает функцию add, которая проверяет, не является ли список пустым. В случае пустого списка создается новый элемент, и на первую позицию сохраняется только что введенное значение. Если список непустой, то введенное значение добавляется в конец последнего элемента. Затем вызывается метод split (Elem*). В теле этого метода содержится проверка на то, не является ли массив указателей переполненным. Для этого в каждом элементе списка есть переменная size. По достижении переменной size размера N, создаётся ещё один элемент списка, содержащий массив указателей, в который переписывается половина указателей из текущего. 2. К следующему: последовательное движение по списку 3. В начало/конец: движение к первому/последнему элементу списка 4. Информация: вывод на экран текущего состояния структуры 5. Главное меню: возврат к главному меню Добавить с сохранением порядка: После считывания информации программа ищет место вставки. Для этого в теле функции реализовано два цикла. Первый цикл лидет по элементам списка и ищет тот в который необходимо вставить значение. Второй цикл лидет уже по элементам массива указателей в найденном ранее элементе списка. После того, как место вставки найдено, программа лраздвигает элементы массива (при этом учитывается, что может наступить переполнение) и вставляет на освободившееся место введенное значение. Эта функция используется при сортировке и балансировке. Добавить по логическому номеру ( ЛН ) Программа переводит введенный логический номер в индекс объекта массива указателей в соответствующем элементе списка. Таким образом получаем место вставки. Далее лраздвигаем элементы массива указателей и вставляем на освободившееся место. При этом также как и в функции вставки с сохранением упорядоченности, идет проверка на переполнение массива указателей и если это происходит, то создается еще один элемент списка, в него переписывается половина указателей из текущего и соответствующим образом происходит переброска указателей. Вывод по ЛН Также как и в функции вставки по логическому номеру происходит его перевод в индекс элемента в массиве указателей. Затем на экран выводится только объект соответствующий этому номеру. Затем спрашивается, хочет ли пользователь удалить выбранный элемент, и если нажимается клавиша 1, происходит удаление этого объекта. Удалить по ЛН После нахождения соответствующего элемента происходит лсжатие массива указателей относительно этого элемента. Таким образом он оказывается затертым. После удаления функция уменьшает переменную size на единицу. Удалить весь список При выборе этого пункта меню происходит вызов функции delList(), которая освобождает память и обнуляет указатели на первый и последний элементы списка. Сортировка Функция сортировки основана на функции вставки с сохранением упорядоченности. Сначала функция переписывает все элементы списка в массив, затем удаляет список и производит включение с сохранением упорядоченности элементов массива в новый список. По завершении операции массив удаляется. Балансировка Очень похожа на сортировку. Единственное отличие в том, что элементы добавляются не с сохранением порядка, а просто в конец списка. Сохранение в бинарный файл Производит запись содержимого списка в бинарный файл. Загрузка из бинарного файла Считывает значения из бинарного файла и заносит их в список. Информация о структуре Выводит на экран текущее состояние структуры Выход Завершает работу программы.
  1. Руководство пользователя
После запуска программы пользователь видит перед собой системное меню. Выбор интересующего действия осуществляется нажатием цифровой клавиши, соответствующей номеру пункта меню. Если список пуст, его непосредственный просмотр невозможен, нужно сначала ввести какое-нибудь значение. Для этого выбираем пункт меню лпросмотр списка. После этого на экране появляется запрос на ввод данных. Необходимо ввести числа или строки (в зависимости от типа) и нажать лenter. После этого открывается доступ на просмотр списка. Необходимо заметить, что при переполнении текущего элемента автоматически будет создан новый, в который будет переписана половина значений из старого. Теперь можно вывести элементы списка на экран. Пункты меню лУдалить по ЛН и лУдалить весь список служат для удаления элементов или же всего списка. Причем список можно очистить, поочередно удаляя все элементы, а можно сразу. Пункт меню лСортировка служит для сортировки по алфавиту (значению) введенных значений (в зависимости от типа). Во время сортировки структуры данных автоматически балансируются. Балансировку можно выполнить и вручную Для этого необходимо выбрать пункт меню лБалансировка. Пункты меню лСохранение и лЗагрузка служат для чтения и записи данных в двоичный файл. В обоих случаях будет предложено ввести имя файла. Завершение работы программы происходит по выбору пункта лВыход (нажатие клавиши Esc)
  1. Ограничения в работе программы.
Из-за несовершенства алгоритма сортировки на выполнение этой функции уходит много времени. Также ошибки возникают при попытке ввести произвольный набор символов в качестве нового элемента, когда установлен тип данных int (либо другие типы Ц double, float и т.д.), т.к. корректность вводимых данных не отслеживается. 9. Текст программы //----------------------classes.h------------------ #include <iostream.h> #include <conio.h> #include <string.h> #include <stdio.h> #include <windows.h> #include <fstream.h> char* rus (char*); // вывод кириллического шрифта void Report (int); // отчет об обнаруженных ошибках //----------------------------------------- template <class T, int N> class List { int kol; // количество элементов в списке public: class Elem // вложенный класс { public: friend class List; T *data; int size; Elem *next; // указатель на следующий элемент списка Elem () { data= new T[N+1]; data[0]=NULL; size=0; next=NULL;} // конструктор ~Elem() { delete []data; size=0; next=NULL;} // деструктор }; Elem *first, *last; // границы списка List () { first=last=NULL; kol=0;} // конструктор списка List (List *t) { first= t.first; last= t.last; kol= t.kol;} ~List () { this->delList(); } // деструктор списка //------------------------ void split (Elem *p) // разбиение элемента списка при переполнении { if (p->size < N) return; // если разбиение не требуется Elem *a= new Elem; // создаем новый элемент списка a->next= p->next; p->next= a; for(int i=0;i<N/2;i++) { a->data[i] = p->data[i+N/2]; // переписываем половину указателей p->data[i+N/2] = NULL; // удаляем из старого } a->size= p->size= N/2; p->data[N/2] = NULL; a->data[N/2] = NULL; kol++; // увеличиваем внутрен. счетчик списка if(last==p) last=a; // если последний } //------------------------ long count (Elem *end) // подсчет кол-ва объектов до указанного элемента списка { Elem *p=first; for(long n=0; p!=NULL && p!=end; n+=p->size, p=p->next); return n; } //------------------------ void info () // информация о структуре { long n1= count(last) + (first?(last->size):0); double fill = ((kol==0 || n1==0)? 0.0 : (100.0*n1)/(kol*N)); // подсчет процента заполнения system("cls"); cout << rus("-----------Информация о структуре---------\n"); cout << rus("Количество элементов в списке: ") << kol << endl; cout << rus("Количество строк в списке: ") << n1 << endl; cout << rus("Процент заполнения структуры: ") << fill << "%\n"; cout <<"-------------------------------------------------------------" << endl; } //------------------------ void add (T val) // добавление строк { if (first == NULL) // если список пуст Ц добавляем значение первым { Elem *ph= new Elem; ph->next= NULL; kol=1; ph->data[0]= val; ph->size++; first= last= ph; return; } Elem *p= last; // иначе в конец p->data[p->size] = val; p->size++; split (p); } //------------------------ void insOrd (T val) // вставка с сохранением порядка { if (first == NULL) { Elem *ph=new Elem; first= last= ph; ph->next=NULL; kol=1; ph->data[0]= val; ph->size++; return; } int i,k; Elem *q= first; // поиск места вставки for(;q->next!=NULL && (q->data[q->size-1] <= val); q=q->next); for(i=0; i< q->size && q->data[i] != NULL && (q->data[i] <= val); i++); for(k=q->size; k>i; k--) q->data[k] = q->data[k-1]; // раздвижка границ q->data[i]= val; q->size++; // увеличить счетчик кол-ва указателей split(q); // проверить на переполнение } //------------------------ void insLog (T val, int n) // вставка по логическому номеру { if (first->data[0] == NULL) throw 0; // список пуст long cnt = count(last) + last->size; if (n<0 || n>cnt-1 || cnt<=0) throw 6; // некорректный ЛН int j; Elem *q= first; while(q!=NULL) { if(n<q->size) break; n-=q->size; q= q->next; } if(q==NULL) throw 6; for(j= q->size; j>n; j--) q->data[j] = q->data[j-1]; // раздвижка границ q->data[n]= val; q->size++; split(q); } //------------------------ void delDataLog (int n) // удаление по ЛН { if (first->data[0] == NULL) throw 1; // список пуст long cnt = count(last) + last->size; if (n<0 || n>cnt-1) throw 6; // некорректный ЛН int i; Elem *q= first; while(q!=NULL) { if(n < q->size) break; n-=q->size; q=q->next; } if(q==NULL) return; for(i=n; i < q->size - 1; i++) q->data[i]= q->data[i+1]; // лсдвижка границ q->size--; // уменьшить кол-во указателей в тек. элементе списка } //------------------------ void ShowDataLog (int num) // вывод по ЛН { if (first->data[0] == NULL) throw 2; // список пуст long cnt = count(last) + last->size; int n=num; if (n<0 || n>cnt-1) throw 6; // некорректный ЛН Elem *q= first; while(q!=NULL) { if(n < q->size) break; n -= q->size; q = q->next; } if(q == NULL) return; cout << rus("Запрошенный элемент найден: ") << q->data[n] << endl; cout << rus("Нажмите '1', если хотите удалить его...") << endl; if ('1' == getch()) delDataLog (num); // удалить найденный элемент } //------------------------ void delList () // очищение всего списка { if(first==NULL) return; // список пуст Elem *pt=first; // начиная с первого элемента while(pt) { first= first->next; delete pt; // удалить теущий pt= first; // запомнить следующий } first= last= NULL; // обнулить всё kol= 0; } //------------------------- void sort () // сортировка списка { if (first == NULL) throw 3; // список пуст int i,j=0; long s = count(last) + (last->size); T *mas = new T[s + 1]; // создать массив для хранения указателй if (!mas) throw 7; // не хватило памяти! Elem *pt= first; while(pt!=NULL) { for( i=0; pt->data[i]!=NULL && j<s; i++ ) mas[j++] = pt->data[i]; pt= pt->next; } mas[j] = NULL; delList(); // очищение списка for(i=0; mas[i] != NULL; i++) insOrd (mas[i]); // упоряд. вставка delete []mas; } //------------------------ void save_bin (char *path) // сохранение в бинарный файл { ofstream out(path, ios::binary); // связывание с указанным файлом if(!out) // файл недоступен { out.close(); throw 5; } Elem *ph= first; long number = count(last) + last->size; out.write((char*)&number, sizeof(long)); // записать общее кол-во строк while(ph) { for (int i=0; ph->data[i]!=NULL; i++) out.write ((char*)&ph->data[i], sizeof(T)); // .сами строки ph= ph->next; } out.close (); // оборвать связь с файлом } //--------------------------------- void load_bin (char* path) // загрузка из бинарного файла { ifstream in(path, ios::binary|ios::nocreate); // связь с файлом if (!in) { in.close(); remove (path); // файл не существует throw 5; } T obj; long number; in.read((char*)&number, sizeof(long)); // прочитать кол-во строк for(long i=0; i<number; i++) { in.read ((char*)&obj, sizeof(T)); // прочитать сами строки add (obj); // добавить в список } in.close(); // оборвать связь с потоком } //--------------------------------- void balance () { if (!first) throw 4; save_bin ("tmp"); // записать список в промежуточный файл(ПФ) delList (); // очистка списка load_bin ("tmp"); // загрузить из ПФ remove ("tmp"); // удалить ПФ } }; //-----------------END OF LIST--------- //-------------Digit.h-------------------------- #include <iostream.h> #include <string.h> class Digit // класс собственной разработки { friend ostream& operator<<(ostream& , Digit& ); // вывод в поток friend istream& operator>>(istream& , Digit& ); // ввод(чтение) из потока friend void List<Digit,8>::save_bin(char*); friend void List<Digit,8>::load_bin(char*); char *str; public: // логические бинарные операции int operator< (const Digit& a) { return (strcmp(str, a.str)<0?1:0);} int operator<=(const Digit& a) { return (strcmp(str, a.str)<=0?1:0);} int operator==(const int) {return (str==NULL);} int operator!=(const int) {return (str!=NULL);} Digit operator=(const int) { return Digit();} Digit () { str = NULL;} // конструктор ~Digit () { if(str) delete []str;} // деструктор //------------------ Digit (char *a) // конструктор с параметрами { str = new char[strlen(a)+1]; strcpy (str,a); } //------------------ Digit (Digit& OLD) // конструктор копирования { str= new char[strlen(OLD.str)+1]; strcpy (str,OLD.str); } //------------------ Digit& operator=(const Digit& a) // переопред. операция присваивания { str=new char[strlen(a.str)+1]; strcpy(str, a.str); return *this; } }; //------------End of DigiT----------------- //-------------------------- //---friend Digit functions ostream& operator<<(ostream& os, Digit& a) { os << a.str; return os; } //-------------------------------------- istream& operator>>(istream& is, Digit& a) { char *s= new char[80]; is >> s; a.str = new char[strlen(s)+1]; strcpy (a.str, s); delete []s; return is; } //-------------------------------------- void List <Digit,8>::save_bin(char* path) { ofstream ofile(path,ios::binary|ios::out); if (!ofile) { ofile.close(); throw 5; } Elem *pt=first; long kol = count(last)+last->size; ofile.write((char*)&kol,sizeof(kol)); while(pt) { for(int i=0; pt->data[i]!=NULL && i<pt->size; i++) { ofile.write((pt->data[i]).str, strlen((pt->data[i]).str)); ofile.write(" ",1); } pt=pt->next; } ofile.close(); } //-------------------------------------- void List <Digit,8>::load_bin(char* path) { ifstream in(path,ios::in|ios::binary); if (!in) { in.close(); remove (path); throw 5; } Digit *t; char *m=new char[80]; long kol; in.read((char*)&kol,sizeof(long)); for (int i=0; i < kol; i++) { in >> m; t = new Digit(m); add(*t); } delete []m; in.close(); } //----------------func.cpp--------------- #include "classes.h" char* rus(char* msg) { char* s= new char[strlen(msg)+1]; CharToOem (msg,s); // эта ф-я обеспечивает вывод кириллицы return s; } //-------------------------------- void Report (int code) // вывод отчета об ошибках { char *error[] = { "Список пуст. Нет элементов для вставки", "Список пуст. Нет элементов для просмотра.", "Список пуст. Нет элементов для удаления.", "Список пуст. Сортировка невозможна", "Список пуст. Балансировка невозможна ", "Не могу открыть файл", "Неверный ЛН", "Нехватка памяти", NULL }; cout << rus( error[code] ) << endl; getch (); } //---------------demo.cpp ( содержит main )-------------- #include "classes.h" #include "digit.h" #define MODE //--------------- int main () { #ifdef MODE // тип данных - int List <int,10> L; List <int,10>::Elem *p; int val; #else // тип данных - Digit List <Digit,8> L; List <Digit,8>::Elem *p; //char *val= new char[30]; Digit val; #endif char *name= new char[50]; int num=0, cnt=0, exit=0, k=0; while(1) { try { //---------TRY BLOCK-------------- cnt = L.count(L.last) + ((L.first!=NULL)?((L.last)->size) : 0); // общее кол-во строк system("cls"); cout << rus("1. Показать список\n2. Добавить по ЛН\n3. Удалить по ЛН\n"); cout << rus("4. Удалить по ЛН\n5. Удалить весь список\n"); cout << rus("6. Сохранение в бинарный файл\n"); cout << rus(У7. Загрузка из бинарного файла.\n"); cout << rus("8. Балансировка\n9. Сортировка\nF1. Информация о структуре\n"); cout << "------ " << rus("[Esc] - ВЫХОД") << " ------" << endl; switch( getch() ) { case '1': if(!L.first) { cout << rus("Список пуст. Введите data: "); cin >> val; L.add (val); } p = L.first; exit=0; while (!exit) { system("cls"); k = L.count(p); cout << rus("кол-во: ") << p->size << "\n" << endl ; for(int j=0; j < p->size; j++) cout << "[" << j+k << "]= " << p->data[j] << endl; cout << endl; cout << rus("<Insert> - добавление Ф); сout << rus(У<Home> - в начало"); cout << rus("<F1> - информация\nФ); сout << rus("<Space> - к следующему "); cout << rus("<End>-в конец <Esc> - главное меню\n"); cout << endl; switch( getch() ) { case 71: p = L.first; break; // Home case 79: p = L.last; break; // End case 27: exit = 1; break; // Esc case 32: if(p->next) p = p->next; break; // Space case 59: L.info(); getch(); break; // F1 case 82: cout << rus("Введите data: "); // Insert cin >> val; L.add (val); break; } } break; case '2': if (cnt == 0) throw 0; cout << rus("Введите data: "); cin >> val; cout << rus("\Доступные ЛН 0...") << cnt-1 << endl; cout << rus("\nВведите ЛН (-1 для выхода): "); cin >> num; if (num <0 || num > cnt-1) break; L.insLog (val,num); break; case '3': if (cnt == 0) throw 1; cout << rus("\n Доступные ЛН 0...") << cnt-1 << endl; cout << rus("\nВведите ЛН (-1 для выхода): "); cin >> num; L.delDataLog (num); break; case '4': if (cnt==0) throw 2; cout << rus("Введите ЛН: "); cin >> num; L.ShowDataLog (num); break; case '5': L.delList(); cout << rus("Список удален!") << endl; getch(); break; case '6': cout << rus("Укажите файл для записи: "); cin >> name; L.save_bin (name); break; case '7': cout << rus("Укажите файл для чтения: "); cin >> name; if (L.first) { cout << rus("Список не пуст.Ф); сout << rus(" Удалить старый список? (да-'1')") << endl; if ('1' == getch()) { cout << rus("Список удален!") << endl; L.delList (); getch(); } } L.load_bin (name); break; case '8': L.balance (); break; case '9': L.sort (); break; case 59: L.info (); getch(); break; case 27: delete []name; return 1; } //-------END of Try Block---------- } catch (const int code) { Report (code); } } return 0; }