К ним относятся разнообразные операции, которые приходится применять сразу перед или сразу после операции ввода-вывода. Например:
cout << x; cout.flush(); cout << y;
cin.eatwhite(); cin >> x;
Если писать отдельные операторы как выше, то логическая связь между операторами неочевидна, а если утеряна логическая связь, программу труднее понять.
Идея манипуляторов позволяет такие операции как flush() или eatwhite() прямо вставлять в список операций ввода-вывода. Рассмотрим операцию flush(). Можно определить класс с операцией operator<<(), в котором вызывается flush():
class Flushtype { };
ostream& operator<<(ostream& os, Flushtype) { return flush(os); }
определить объект такого типа
Flushtype FLUSH;
и добиться выдачи буфера, включив FLUSH в список объектов, подлежащих выводу:
cout << x << FLUSH << y << FLUSH ;
Теперь установлена явная связь между операциями вывода и сбрасывания буфера. Однако, довольно быстро надоест определять класс и объект для каждой операции, которую мы хотим применить к поточной операции вывода. К счастью, можно поступить лучше. Рассмотрим такую функцию:
typedef ostream& (*Omanip) (ostream&);
ostream& operator<<(ostream& os, Omanip f) { return f(os); }
Здесь операция вывода использует параметры типа "указатель на функцию, имеющую аргумент ostream& и возвращающую ostream&". Отметив, что flush() есть функция типа "функция с аргументом ostream& и возвращающая ostream&", мы можем писать
cout << x << flush << y << flush;
получив вызов функции flush(). На самом деле в файле <iostream.h> функция flush() описана как
ostream& flush(ostream&);
а в классе есть операция operator<<, которая использует указатель на функцию, как указано выше:
class ostream : public virtual ios { // ... public: ostream& operator<<(ostream& ostream& (*)(ostream&)); // ... };
В приведенной ниже строке буфер выталкивается в поток cout дважды в подходящее время: