![]() Главная страница Случайная страница КАТЕГОРИИ: АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника |
Ostream_iterator
++r инкремент (префиксная форма) r++ инкремент (постфиксная форма)
Обычно эти итераторы используются вместе с iostreams-классами и стандартными алгоритмами. Итератор ostream_iterator предназначен только для последовательно выполняемой записи. После доступа к некоторому элементу программист не может вернуться к нему опять, не повторив всю итерацию сначала. При использовании этих итераторов канал обрабатывается как последовательный контейнер. Это означает, что при связывании канала с iostreams-объектами посредством итератора ostream_iterator и файловых дескрипторов мы можем применить стандартный алгоритм обработки данных для ввода их из канала и вывода их в канал. Причина того, что эти итераторы можно использовать вместе с каналами, состоит в связи, которая существует между итераторами и iostreams-классами. На рис. 11.10 представлена диаграмма, отображающая отношения между итераторами ввода-вывода и iostreams-классами.
На рис. 11.10 также показано, как эти классы взаимодействуют с объектно-ориентированным каналом. Рассмотрим подробнее, как итератор ostream_iterator используется с объектом класса ostream. Если инкрементируется указатель, мы ожидаем, что он будет указывать на следующую область памяти. Если же инкрементируется итератор ostream_iterator, он переме щ ается на следующую позицию выходного потока. Присваивал значение разыменованному указателю, мы тем самым помещаем это значение в область, на которую он указывает. Присваивал значение итератору ostream_iterator, мы помещаем это значение в выходной поток. Если выходной поток связан с объектом cout, это значение отобразится на стандартном устройстве вывода. Мы можем объявить объект класса ostream_iterator следующим образом, ostream_iterator< int> X(cout, «\n»); Тогда X является объектом типа ostream_iterator. При выполнении операции инкремента X++; итератор X перейдет к слелую щ ей позиции выходного потока. А при выполнении этой инструкции присваивания *X = Y; значение Y будет отображено на стандартном устройстве вывода. Дело в том, что оператор присваивания " =" перегружен дл я использования объекта класса ostream. В результате объявления ostream_iterator< int> X(cout, «\n»); будет создан объект X с использованием аргумента cout. Второй аргумент в конструкторе является разделителем, который автоматически будет размещаться после каждого int -значения, вставляемого в поток данных. Объявление итератора ostream_iterator выглядит следующим образом (листинг 11.22). // Листинг 11.22. Объявление класса ostream_iterator template < class _Tp> class ostream_iterator { protected: ostream* _M_stream; const char* _M_string; public: typedef output_iterator_tag iterator_category; typedef void value_type; typedef void difference_type; typedef void pointer; typedef void reference; ostream_iterator(ostream& _s): _M_stream(& _s), _M_string(0) {} ostream_iterator(ostream& _s, const char* _с): _M_s tream (& _s), _M_string (_с) { } ostream_iterator< _Tp> & operator=(const _Tp& _value) { *_M_stream < < _value; if (_M_string){ *_M_stream < < _M_string; return *this; } ostream_iterator< _Tp> & operator*() { return *this; } ostream_iterator< _Tp> & operator++() { return *this; } ostream_iterator< _Tp> & operator++(int) { return *this; } }; Конструктор класса ostream_iterator принимает ссылку на объект класса ostream. Класс ostream_iterator находится с классом ostream в отношении агрегирования. Назначение класса istream_iterator прямо противоположно классу ostream_iterator. Он используется с объектами класса istream (а не с объектами класса ostream). Если объекты классов istream_iterator и ostream_iterator связаны с iostream-объектами, которые в свою очередь связаны с файловыми дескрипторами канала, то при каждом инкрементировании итератора типа istream_iterator из канала будут считываться данные, а при каждом инкрементировании итератора типа ostream_iterator в канал будут записываться данные. Чтобы продемонстрировать, как эти компоненты работают вместе, рассмотрим две программы (11.2 и 11.2.1), в которых используются анонимные каналы связи. Про-грамма11.2 представляет родительский процесс, а программа11.2.1— сыновний. В»родительской» части для создания сыновнего процесса используются системные функции fork() и execl (). При том, что файловые дескрипторы наследуются сыновним процессом, их значения незамедлительно становятся достоянием программы 11.2.1 благодаря вызовуфункции execl(). // Программа 11.2 10 int main(int argc, char *argv[]) 11 { 13 int Size, Pid, Status, Fdl[2], Fd2[2]; 14 pipe(Fdl); pipe(Fd2); 15 strstream Buffer; 16 char Value[50]; 17 float Data; 18 vector< float> X(5, 2.1221), Y; 19 Buffer «Fdl[0] «ends; 20 Buffer» Value; 21 setenv(«Fdin», Value, l); 22 Buffer.clear(); 23 Buffer «Fd2[l] «ends; 24 Buffer» Value; 25 setenv(«Fdout», Value, l); 26 Pid = fork(); 27 if(Pid! = 0){ 28 ofstream OPipe; 29 OPipe.attach(Fdl[l]), - 30 ostream_iterator< float> OPtr(OPipe, " \n»); 31 OPipe «X.size() «endl; 32 copy(X.begin(), X.end(), OPtr); 33 OPipe «flush; 34 ifstream IPipe; 35 IPipe.attach(Fd2[0]); 36 IPipe» Size; 37 for(int N = 0; N < Size; N++) 38 { 39 IPi ре» Data; 40 Y.push_back(Data); 41 } 42 wait(& Status); 43 ostream_iterator< float> OPtr2(cout, " \n»); 44 copy(Y.begin(), Y.end(), OPtr2); 45 OPipe.close(); 46 IPipe.close(); 47 } 48 else{ 49 execl("./programll-2b», «programll-2b», NULL); 50 } 51 52 return(0); 53 } В строках 21 и 25 системнал функция setenv () используется для передачи значений файловых дескрипторов сыновнему процессу. Это возможно благодаря тому, что сыновний процесс наслелует среду родительского процесса. Мы можем устанавливать переменные среды в программе с помощью вызова функции setenv (). В данном случае мы устанавливаем их следующим образом. Fdin=filedesc; Fdout=filedesc; Сыновний процесс затем использует системный вызов getenv() для считывания значений переменных Fdin и Fdout. Значение переменной Fdin будет представлять «считывающий конец» канала для сыновнего процесса, а значение переменной Fdout — «записывающий». Использование системных функций setenv () и getenv() обеспечивает просгую форму межпроцессного взаимодействия (interprocess communication — IPC) между родительским и сыновним процессами. Каналы создаются при выполнении инструкций, приведенных в строке 14. Родительский процесс присоединяется к одному концу канала для операции записи с помощью метода attach() (строка29). После присоединения любые данные, помещенные в объект OPipe типа ofstream, будут записаны в канал. Итератор типа ostream_iterator подключается к объекгу OPipe при выполнении следующей инструкции (строка 30): ostream_iterator< float> OPtr(OPipe, " \n»); Теперь итератор OPtr ссылается на объект OPipe. После каждой порции помещаемых в канал данных будет вставляться разделитель " \n». С помощью итератора OPtr мы можем поместить в канал любое количество float -значений. При этом мы можем связать с каналом несколько итераторов различных типов. Но в этом случае необходимо, чтобы на «считывающем» конце канала данные извлекались с использованием ите раторов соответствующих типов. При выполнении слелующей инструкции из программы 11.2 в канал сначала помещается количество элементов, подлежащих передаче: OPipe «X.size() «endl; Сами элементы отправляются с использованием одного из стандартных С++-алгоритмов:
|