Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Visual_C_console.pdf
Скачиваний:
34
Добавлен:
16.05.2015
Размер:
954.14 Кб
Скачать

147 strcpy(pt2, ptemp);

}

}

}

}

Связные списки

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

Для обработки списков определяются следующие основные операции:

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

Вставка элемента перед заданным элементом.

Исключение заданного элемента.

Объединение двух или более списков в один список.

Выделение из списка подмножества списка. Создание копии списка.

Определение количества элементов списка.

Сортировка элементов списка по заданным полям.

Поиск в списке элемента, удовлетворяющего условиям поиска.

Часто доступ к первому и последнему элементам (а также к следующему или предыдущему) списка осуществляется проще, чем к промежуточным элементам в произвольном порядке.

Существуют списки, в которых доступ (а также исключение или вставка) выполняется только для первого или последнего элемента. Такие списки имеют специальные названия:

Стек (stack) - линейный список, в котором доступ осуществляется только с одного конца списка.

148

Очередь (queue) - линейный список, в котором все включения элементов выполняются на одном конце списка, а исключения на противоположном конце списка.

Двусторонняя очередь (deque) - линейный список, в котором включения элементов выполняются с обоих концов списка.

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

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

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

Для построения связных списков используются структуры (или классы), которые содержат указатели на структуры этого же типа. Это возможно, поскольку длина указателя не зависит от типа данных, с которым этот указатель связан.

В однонаправленных связных списках каждый элемент содержит указатель на следующий элемент. Двунаправленные списки имеют указатели на следующий и предыдущий элементы.

Контейнерные классы

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

149

коллекцию нового экземпляра, исключение экземпляра из коллекции, просмотр содержимого экземпляров, получение информации о количестве экземпляров в коллекции и т. д.

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

Контейнеры можно разделить на две группы:

Последовательные контейнеры.

Ассоциативные множества (ассоциативные контейнеры).

Основных последовательных контейнеров три:

Вектор (заголовочный файл <vector>).

Связный список (заголовочный файл <list>).

Двусторонняя очередь (заголовочный файл <deque>)

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

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

В связных списках каждый элемент имеет указатель на следующий элемент, поэтому элементы в памяти могут размещаться в произвольном порядке, а, следовательно, для вставки элемента в сере-

150

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

Двусторонняя очередь позволяет добавлять (и исключать) экземпляры, как в начало списка, так и в его конец.

Для объявления контейнеров требуется указать пространство имен с помощью оператора:

using namespace std;

Независимо от типа и вида контейнера интерфейс для работы с контейнером стандартизован в том смысле, что одинаковые по назначению операции и параметры являются одинаковыми для всех контейнеров.

Для обеспечения доступа к экземплярам контейнера используются итераторы, представляющие собой указатели на экземпляры. Итератор объявляется для контейнера и позволяет обеспечить последовательный доступ к экземплярам и доступ к конкретным экземплярам. Для получения значения экземпляра для данного итератора применяют операцию разадресации (например, если итератор объявлен как it, то для получения значения экземпляра применяется запись ٭it), но можно обращаться и по индексу. Операция разадресации доступна не для всех типов данных, помещенных в контейнер.

Если в приложении имеется несколько однотипных контейнеров, то для работы с ними можно использовать один и тот же итератор. Для настройки итератора на первый экземпляр вектора применятся функция begin(), а для настройки на последний экземпляр end().

151

Пример:

vector <string> svec;// Объявление контейнера для класса string // Объявление итератора для этого контейнера

vector <string> ::iterator it;

Для указанного объявления контейнера оператор цикла для прохода по всем экземплярам контейнера будет иметь вид:

for (it = svec.begin();.it < svec.end(); it++)

Функции begin() и end() входят в класс итератора, но, как было указано выше, итератор может обслуживать несколько однотипных контейнеров, поэтому следует указывать контейнер, с которым в данный момент связан итератор.

Для всех последовательных контейнеров одинаковыми являются следующие операции:

Вставить экземпляр (например, строку символов) в конец контейнера (функция push_back(), например, чтобы вставить строку в объявленный выше контейнер следует записать svec.push_back(“Иванов И. И.”);).

Извлечь последний экземпляр контейнера (pop_back()).

Вставить в произвольное место (insert())

Удалить из произвольного места (erase()).

Получить размер контейнера (size()).

Получить первый элемент (front()).

Получить последний элемент (back()).

Для контейнеров типа vector и deque существует операция получения значения экземпляра по индексу ([ ] или at())и другие.

ВMSDN указано, что последовательные контейнеры содержат элементарные типы данных. В принципе контейнер может содержать и объекты класса при ограничении, что класс не является производным от какого-нибудь класса библиотеки MFC. Это связано с тем, что для создания объекта класса для помещения в контейнер требуются три конструктора: конструктор без параметров обеспечивает выделение памяти для помещаемого в контейнер объекта, конст-

152

руктор с параметрами выполняет формирование объекта класса, но требуется также конструктор копирования, который позволяет поместить данные объекта в контейнер. Однако стандартный конструктор копирования пользователю не доступен.

Ассоциативные контейнеры представляют собой коллекции, в которых экземпляры коллекции отсортированы по какому-либо ключу. Существуют следующие основные виды ассоциативных контейнеров:

Словари (заголовочный файл <map>).

Словари с дубликатами (заголовочный файл <milliamp>).

Множества (заголовочный файл <set>).

Множества с дубликатами (заголовочный файл <multiset>).

Для работы с этими контейнерами требуется заголовочный файл

<utility>.

Ассоциативные контейнеры предназначены, в основном, для поиска (словари и глоссарии) и получения списков для выбора и проверки (множества). Решение этих задач предусматривает поиск по ключу, при котором выполняется сравнение заданного ключевого выражения с экземплярами ассоциативного контейнера. Для обеспечения таких сравнений в ассоциативном контейнере перегружены знаки логических отношений (<, <=, >, != и т. д.). Эти отношения позволяют обеспечить выборку не только по условию равенства, но и более сложные выборки.

Поскольку экземпляры коллекции отсортированы, то скорость доступа к экземплярам высокая.

Ниже приведена программа создания глоссария из файла. Каждый экземпляр глоссария описан как переменная типа string. Этот тип данных допускает наличие внутри белых пробелов, поэтому отпадает потребность объявлять для экземпляра глоссария переменную типа структуры, то есть, один элемент типа string позволяет хранить в себе несколько фактических строк, видимых на экране. Другими словами, переменная типа string позволяет хранить абзац. В

153

данной программе предусмотрено, что для отделения ключевого слова от объяснения каждый экземпляр глоссария в файле начинается с символа ‘@’, а ключ заканчивается символом ‘#’. Поскольку функция getline считывает абзац до заданного разграничителя, то надо исключить символ ‘@’в самом начале файле (его можно использовать для проверки формата файла, чтобы не начинать обработку любых файлов, непригодных для данной программы). Для исключения первого символа файла его достаточно прочитать функциий get. После такого считывания текущая позиция перемещается на следующий символ в файле. В этом случае считывание до разграничителя ‘#’позволяет прочитать ключевое слово, а если после этого читать до разграничителя ‘@’, то будет выполнено считывание абзаца объяснения глоссария. Если при этом цикл запустить до конца файла, то будет выполнено чтение произвольного количества экземпляров глоссария. Операции про выделению памяти и сортировке элементов возложено на контейнер.

#include "stdafx.h" #include <string> #include "conio.h" #include <iostream> #include <fstream>

#include <map> // Подключение контейнера для глоссария using namespace std;

#define FNAME "Text.txt"

//Объявление нового имени шаблона glos для глоссария typedef map <string, string, less<string> > glos;

glos glossary; // Объявление имени переменной для глоссария

//Файл открывается глобально для доступности во всех функциях ifstream incoming(FNAME, ios::in);

void read_gloss_items(); // Создание глоссария из файла void write_gloss_item(string str); // Запрос по ключу из глоссария void write_entire_glossary(); // Вывод всего глоссария

int _tmain()

154

{

char ch; string str;

if (!incoming) // Проверка открытия файла

{

cout << "Нельзя открыть файл: " << FNAME << endl; _getch();

return 1;

}

//Чтение с исключением 1-го символа для проверки формата файла ch=incoming.get(); //Чтение и удаление 1-го символа

if (ch != '@')

{

cout << "\nНеправильный формат файла " << FNAME << endl;;

_getch(); exit(1);

}

read_gloss_items(); // Создание глоссария из файла write_entire_glossary(); // Вывод на экран всего глоссария cout << "\nВведите ключ для поиска \n";

cin >> str;

write_gloss_item(str);// Вывод экземпляра глоссария (ключ str)

_getch(); incoming.close(); return 0;

}

void read_gloss_items()

{

string key_str, str; while (!incoming.eof ())

{

getline(incoming,key_str, '#'); getline(incoming, str, '@');

155 glossary[key_str] = str;

}

}

void write_entire_glossary()

{

glos::iterator i;

for (i = glossary.begin(); i != glossary.end(); i++) cout << << "[Ключ]= "<<i->first <<endl

<< "[ Содержимое ]" << i->second << endl;

}

void write_gloss_item(string str)

{

if (glossary.find(str) != glossary.end())

{

cout<<" Задан ключ: "<< str <<endl <<

" Содержимое:" << endl; cout<< glossary[str]<< endl;

}

if (glossary.find(str) == glossary.end()) cout << Задан ключ: "<< str <<

" Экземпляр не найден << endl;

}

Примечание: При создании глоссария на русском языке надо внимательно следить за кодировкой. Поскольку в среде разработки Visual С++ по умолчанию указывается кодировка

Cyrillic (Windows) – Codepage 1251, а в файлах зачастую применяется кодировка Cyrillic (DOS) – Codepage 866, то при несовпадении кодировок могут возникать ошибки из-за различий в кодировке символов.

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