
- •1.2 Философские замечания
- •1.3 Процедурное программирование
- •1.4 Модульное программирование
- •1.5 Абстракция данных
- •1.6 Пределы абстракции данных
- •1.7 Объектно-ориентированное программирование
- •1.8 Концепции объектно-ориентированного программирования
- •1.8.1 Инкапсуляция
- •1.8.2 Полиморфизм
- •1.8.3 Наследование
- •1.10 Несколько полезных советов
- •2.2 Перегрузка функций
- •2.3 Перегрузка операторов
- •2.4 Наследование
- •2.5 Конструкторы и деструкторы
- •2.7 Два новых типа данных
- •Глава 3. Классы и объекты
- •3.1 Параметризованные конструкторы
- •3.2 Дружественные функции
- •3.3 Значения аргументов функции по умолчанию
- •3.3.1 Корректное использование аргументов по умолчанию
- •3.4 Взаимосвязь классов и структур
- •3.5 Связь объединений и классов
- •3.6 Анонимные объединения
- •3.7 Inline-функции
- •3.7.1 Создание inline-функций внутри класса
- •3.8 Передача объектов в функции
- •3.9 Возвращение объектов функциями
- •3.10 Присваивание объектов
- •3.11 Конструктор копирования
- •3.12 Массивы объектов
- •3.12.1 Инициализация массивов объектов
- •3.12.2 Создание инициализированных и неинициализированных массивов
- •3.13 Указатели на объекты
- •3.14 Статические члены класса
- •Глава 4. Перегрузка функций и операторов
- •4.1 Перегрузка конструкторов
- •4.2 Локализация переменных
- •4.3 Локализация создания объектов
- •4.4 Перегрузка функций и неопределенность
- •4.5 Определение адреса перегруженной функции
- •4.6 Указатель this
- •4.7 Перегрузка операторов
- •4.8 Дружественная функция-оператор
- •4.9 Ссылки
- •4.9.1 Параметры-ссылки
- •4.9.2 Передача ссылок на объекты
- •4.9.3 Возврат ссылок
- •4.9.4 Независимые ссылки
- •4.9.5 Использование ссылок для перегрузки унарных операторов
- •4.10 Перегрузка оператора []
- •4.11 Создание функций преобразования типов
- •Глава 5. Наследование, виртуальные функции и полиморфизм
- •5.1 Наследование и спецификаторы доступа
- •5.1.1 Спецификаторы доступа
- •5.1.2 Спецификатор доступа при наследовании базового класса
- •5.1.3 Дополнительная спецификация доступа при наследовании
- •5.2 Конструкторы и деструкторы производных классов
- •5.3 Множественное наследование
- •5.4 Передача параметров в базовый класс
- •5.5 Указатели и ссылки на производные типы
- •5.6 Ссылки на производные классы
- •5.7 Виртуальные функции
- •5.8 Для чего нужны виртуальные функции?
- •5.9 Чисто виртуальные функции и абстрактные типы
- •5.10 Виртуальный базовый класс
- •5.11 Раннее и позднее связывание
- •Глава 6. Подсистема динамического выделения памяти
- •6.1 Введение в обработку исключений
- •6.1.1 Перехват всех исключений
- •6.2 Работа с памятью с помощью new и delete
- •6.3 Размещение объектов
- •6.4 Перегрузка new u delete
- •7.1.1 Потоки
- •7.3 Создание собственных операторов вставки и извлечения
- •7.3.1 Создание операторов вставки
- •7.3.2 Перегрузка операторов извлечения
- •7.4 Форматирование ввода/вывода
- •7.4.1 Форматирование с помощью функций-членов класса ios
- •7.4.2 Использование манипуляторов
- •7.5 Создание собственных функций-манипуляторов
- •7.5.1 Создание манипуляторов без параметров
- •7.5.2 Создание манипуляторов с параметрами
- •7.6 Файловый ввод/вывод
- •7.6.1 Открытие и закрытие файлов
- •7.6.2 Чтение и запись в текстовые файлы
- •7.6.3 Двоичный ввод/вывод
- •7.6.4 Определение конца файла
- •7.6.5 Произвольный доступ
- •Глава 8. Ввод/вывод в массивы
- •8.1 Классы ввода/вывода в массивы
- •8.2 Создание потока вывода
- •8.3 Ввод из массива
- •8.4 Использование функций-членов класса ios
- •8.5 Потоки ввода/вывода в массивы
- •8.6 Произвольный доступ в массив
- •8.7 Использование динамических массивов
- •8.8 Манипуляторы и ввод/вывод в массив
- •8.9 Собственные операторы извлечения и вставки
- •8.10 Форматирование на основе массивов
- •Глава 9. Шаблоны и библиотека stl
- •9.1 Функции-шаблоны
- •9.2 Функции с двумя типами-шаблонами
- •9.3 Ограничения на функции-шаблоны
- •9.4 Классы-шаблоны
- •9.5 Пример с двумя типами-шаблонами
- •9.6 Обзор библиотеки stl
- •9.7 Класс vector
- •9.7 Класс string
- •9.8 Класс list
7.5.1 Создание манипуляторов без параметров
Все функции-манипуляторы вывода без параметров имеют следующий формат записи:
ostream& имя_манипулятора(ostream &поток)
{
// код
return поток;
}
Важно отметить, что несмотря на то, что манипулятор имеет один аргумент-указатель на поток, при использовании этого манипулятора в операции вывода в его записи нет аргументов.
В следующей программе создается манипулятор setup(), устанавливающий флаг левого выравнивания, задающий ширину поля равной 10 и устанавливающий знак $ в качестве символа заполнения.
#include <iostream.h>
#include <iomanip.h>
ostream& setup(ostream &stream)
{
stream.setf(ios::left);
stream << setw(10) << setfill('$');
return stream;
}
int main()
{
cout << 10 << " " << setup << 10;
return 0;
}
Имеются две причины для того, чтобы приспосабливать манипуляторы под потребности пользователя. Первая заключается в том, что может понадобиться выполнять операции ввода-вывода на устройство, для которого не существует предопределенных манипуляторов. Примером служит плоттер. В таком случае создание собственного манипулятора делает вывод данных на такое устройство более удобным. Вторая причина заключается в том, что однажды можно обнаружить повторение одной и той же последовательности операций во многих случаях. Можно собрать эти операции в одном едином манипуляторе, как это проиллюстрировано в предыдущей программе.
Общая форма записи функций-манипуляторов ввода без параметров имеет следующий вид:
istream& имя_манипулятора(istream &поток)
{
// код
return поток;
}
Например, следующая программа создает манипулятор prompt() для вывода на экран подсказки и переключения ввода на шестнадцатеричный:
#include <iostream.h>
#include <iomanip.h>
istream& prompt(istream &stream)
{
cin >> hex;
cout << "Enter number using hex format: ";
return stream;
}
int main()
{
int i;
cin >> prompt >> i;
cout << i;
return 0;
}
Принципиально важно то, что манипулятор возвращает поток. Если этого не сделать, то тогда манипулятор не может быть использован в комплексных выражениях ввода/вывода.
7.5.2 Создание манипуляторов с параметрами
Создание функций-манипуляторов, имеющих аргументы, несколько сложнее, чем манипуляторов без аргументов. Одна из причин этого заключается в том, что параметризованные манипуляторы используют классы-макросы, определенные в файле iomanip.h.
Для создания параметризованного манипулятора необходимо включить в файл заголовочный файл iomanip.h. В этом файле определено несколько макросов. Мы будем использовать два из них — OMANIP и IMANIP. Первый используется для создания манипуляторов вывода с аргументами. Второй используется для создания параметризованных манипуляторов ввода.
В общем случае, если необходимо создать манипулятор, имеющий аргументы, то следует создать две перегруженных функции-манипулятора. В одной необходимо определить два параметра. В качестве первого параметра используется ссылка на поток, а вторым является параметр, передаваемый в функцию. Вторая версия манипулятора имеет один параметр — тот, который указывается при использовании манипулятора в выражениях ввода/вывода. Эта вторая версия генерирует вызов первой версии. Общая форма манипуляторов вывода с параметрами имеет вид:
ostream& имя_манипулятора(оstream &поток, тип параметр)
{
// код
return поток;
}
OMANIP(тип) имя_манипулятора(тип параметр)
{
return OMANIP(тип)(имя_манипулятора, параметр);
}
Поскольку omanip является классом-шаблоном, то тип становится типом данных, с которыми оперирует специфический объект omanip, возвращаемый манипулятором.
Следующая программа создает параметризованный манипулятор вывода indent(), осуществляющий отступ на указанное число пробелов.
#include <iostream.h>
#include <iomanip.h>
ostream& indent(ostream &stream, int length)
{
for(int i=0; i<length; i++) cout << " ";
return stream;
}
OMANIP(int) indent(int length)
{
return OMANIP(int)(indent, length);
}
int main()
{
cout << indent (10) << "This is a test\n";
cout << indent(20) << "of the indent manipulator.\n";
cout << indent (5) << "It works!\n";
return 0;
}
Как можно видеть, манипулятор indent() перегружен в соответствии с тем, как это обсуждалось выше. Когда программа доходит до indent(10), выполняется вторая версия indent() со значением 10, переданным в параметре length. Эта версия затем исполняет первую версию с величиной 10, переданной снова в параметре length. Этот процесс повторяется всякий раз при вызове indent().
Манипуляторы для ввода данных также могут иметь параметр. В следующей программе создается манипулятор getpass(), используемый для получения пароля. Он имеет аргумент, в котором указано число попыток, предоставляемых пользователю для корректного ввода пароля.
#include <iostream.h>
#include <iomanip.h>
#include <string.h>
#include <stdlib.h>
char password[] = "I like C++";
char pw[80];
// ввод пароля
istream& getpass(istream &stream, int tries)
{
do
{
cout << "Enter password: ";
stream >> pw;
if(!strcmp(password, pw)) return stream;
tries--;
} while(tries>0);
cout << "All tries failed! \n";
exit(1); // пароль не введен
}
IMANIP(int) getpass(int tries)
{
return IMANIP(int)(getpass, tries);
}
int main()
{
cin >> getpass(3);
cout << "Login Complete! \n";
return 0;
}
Обратим внимание, что формат для манипуляторов ввода тот же самый, что и для манипуляторов вывода, за исключением двух моментов: используется поток ввода istream и указывается макрос IMANIP.
Подобные манипуляторы, приспособленные к конкретному использованию, могут значительно упростить инструкции ввода/вывода и улучшить читаемость кода.