Если инкрементируется указатель, мы ожидаем, что он будет указывать на следующую область памяти. Если же инкрементируется итератор
ostream_iterator, он переме
щ ается на следующую позицию выходного потока. Присваивал значение разыменованному указателю, мы тем самым помещаем это значение в область, на которую он указывает. Присваивал значение итератору
ostream_iterator, мы помещаем это значение в выходной поток. Если выходной поток связан с объектом
cout, это значение отобразится на стандартном устройстве вывода. Мы можем объявить объект класса
ostream_iterator следующим образом,
ostream_iterator X(cout, «\n»);Тогда X является объектом типа ostream_iterator. При выполнении операции инкремента X++; итератор X перейдет к слелую щ ей позиции выходного потока. А при выполнении этой инструкции присваивания
*X = Y;
значение Y будет отображено на стандартном устройстве вывода. Дело в том, что оператор присваивания "=" перегружен дл я использования объекта класса ostream. В результате объявления
ostream_iterator X(cout, «\n»);
будет создан объект X с использованием аргумента cout. Второй аргумент в конструкторе является разделителем, который автоматически будет размещаться после каждого int -значения, вставляемого в поток данных. Объявление итератора ostream_iterator выглядит следующим образом (листинг 11.22).
>// Листинг 11.22. Объявление класса ostream_iterator
>template 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 благодаря вызовуфункции