|
Программирование >> Разработка устойчивых систем
Наследование от контейнеров STL Мгновенное создание последовательности элементов выглядит просто потрясающе. Сразу становится ясно, сколько времени вы раньще тратили попусту на решение этой задачи. Например, многие утилиты читают файл в память, изменяют его и записывают обратно на диск. Теперь с таким же успехом можно упаковать функциональность программы StringVector.cpp в ic/iacc для последующего использования. Но при этом возникает вопрос: нужно ли создать вложенный объект типа vector или воспользоваться наследованием? Общие рекомендации объектно-ориентированного проектирования обычно отдают предпочтение композиции (то есть созданию вложенных объектов) перед наследованием, однако стандартные алгоритмы предполагают реализацию конкретного интерфейса, поэтому наследование часто бывает необходимым. : C07:FileEditor.h Редактирование файла в памяти #ifndef FILEEDITOR H #define FILEEDITOR H #include <iostream> #include <string> linclude <vector> class FileEditor : public std::vector<std::string> { public: void open(const char* filename): FileEditor(const char* filename) { open(filename): } FileEditorO {}: void write(std::ostream& out = std::cout): lendif FILEEDITOR H /:- Конструктор открывает файл и читает его в объект FileEditor, а функция write() выводит вектор строк в любой поток ostream. Обратите внимание на определение аргумента по умолчанию в функции write(). Реализация выглядит достаточно просто: : C07:FileEditor.cpp {0} linclude FileEditor.h linclude <fstream> linclude ../require.h using namespace std: void FileEditor::open(const char* filename) { ifstream in(filename): assure(in. filename): string line: while(getline(in. line)) push back(line): Также можно воспользоваться алгоритмом соруО: void FileEditor::write(ostream& out) { for(iterator w = beginO: w != endO: w++) out *w endl: } /:- Классификация итераторов Итератор представляет собой абстрактную модель. Он работает с различными типами контейнеров, не зная базовой структуры данных этих контейнеров. Итераторы поддерживаются большинством контейнеров, поэтому типы итераторов для заданного контейнера определяются записью Контейнерные адаптеры (стек, очередь и приоритетная очередь) не поддерживают итераторы, поскольку с точки зрения пользователя они не ведут себя как последовательность элементов. В этой программе функции StringVector.cpp просто упакованы в новом классе. Довольно часто эволюция классов происходит именно так - сначала программист пишет программу для решения конкретной задачи, а затем обнаруживает в ней типовые функциональные возможности, которые можно преобразовать в класс. Теперь мы можем переписать программу нумерации строк с использованием класса FUeEditor: : C07:FEd1tTest.cpp {L} FileEditor Использование класса FileEditor #i nclude <sstream> #include FileEditor.h linclude ../require.h using namespace std; int main(int argc. char* argv[]) { FileEditor file: ifCargc > 1) { file.open(argv[l]); } else { fi1e.open( FEdi tTest.cpp ); Операции со строками... int i = 1; FileEditor:;iterator w - file.begin(); while(w !- file.endO) { ostringstream ss: ss i++: *w = ss.strO + : + *V: ++w: Вывод в cout: file.writeO; } III:- Теперь чтение файла выполняется в конструкторе (или в функции ореп()): FileEditor file(argv[l]): А вывод происходит в одной строке (при этом выходные данные по умолчанию направляются в cout): file.writeO; Основной код программы связан с главной задачей, то есть с модификацией файла в памяти. <тип контейнерд>::iterator <тип контейнерд>::const i terator У каждого контейнера имеется функция begin(), которая возвращает итератор, обозначающий начало элементов контейнера, и функция end(), возвращающая конечный итератор, установленный в позицию за последним элементом контейнера. Если контейнер объявлен константным, то функции begin() и end() возврапдают константные итераторы, запрещающие модификацию элементов, на которые они ссылаются (поскольку соответствующие операторы также объявлены константными). Все итераторы поддерживают перемещение к следующему элементу (оператор ++) и сравнения == и !=. Следовательно, перемещение итератора it вперед до последнего элемента производится примерно так: whileCit != pastEnd) { Какие-то операции ++it; Здесь pastEnd - конечный итератор, возвращенный функцией end() класса контейнера. Выборка элемента, на который в данный момент ссылается итератор, производится оператором разыменования *. Существуют две формы вызова функции объекта, хранящегося в контейнере: (*it).f() it->f(): Здесь it - итератор, используемый для перебора элементов, а f() - функция класса объекта, хранящегося в контейнере. Руководствуясь этой информацией, можно создать шаблон, работающи!! с контейнером любого типа. В следующей программе шаблон функции арр1у() вызывает для каждого элемента в контейнере функцию класса, указатель на которую передается в аргументе: : С07:Арр1у.срр Простой перебор элементов #inc1ude <iostream> #include <vector> #include <iterator> using namespace std: tempiate<class Cont. class PtrMemFun> void apply(Cont& c. PtrMemFun f) { typename Cont::iterator it = c.beginO: WhileCit != c.endO) { ((*it).*f)(): Альтернативная форма it++: class Z { int i: public: ZCint ii) : i(ii) {} void gO { i++: } friend ostream&
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |