
- •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.3.2 Перегрузка операторов извлечения
Для того, чтобы перегрузить оператор извлечения — экстрактор, используется тот же самый общий подход, что и для перегрузки операторов вставки — инсертеров. Например, следующий экстрактор осуществляет ввод трехмерных координат. Обратим внимание, что он также осуществляет подсказку пользователю.
// получение трехмерных координат – экстрактор
istream& operator>>(istream &stream, point &obj)
{
cout << "Enter x y z values, separating each with a space: ";
stream >> obj.x >> obj.y >> obj.z;
return stream;
}
Первым параметром должна быть ссылка на объект типа istream. Вторым параметром служит ссылка на объект, принимающий ввод. Поскольку это именно ссылка, то второй аргумент может быть модифицирован при вводе информации. Экстрактор должен возвращать ссылку на объект типа istream.
Общая форма экстрактора имеет вид:
istream& operator>>(istream &поток, класс &объект)
{
// код экстрактора
return stream;
}
Следующая программа демонстрирует экстрактор для объекта класса point:
int main()
{
point a(1, 2, 3);
cout << a;
cin >> a; cout << a;
return 0;
}
Подобно функциям вставки, функции извлечения не могут быть членами класса, с элементами которого они оперируют. Как показано в примере, они могут быть друзьями или просто независимыми функциями. За исключением того, что экстрактор должен возвращать ссылку на объект типа istream, внутри экстрактора можно делать все, что угодно. Однако для лучшей структуризации программы и ее ясности лучше всего ограничить эти действия только операциями ввода.
7.4 Форматирование ввода/вывода
Как известно, с помощью функции printf() можно управлять форматированием информации, выводимой на экран. Например, можно указать ширину полей, левое или правое выравнивание и т.д. Все те же типы форматирования можно осуществить и в рамках ввода/вывода языка C++. Имеется два способа форматирования вывода. Первый из них использует функции-члены класса ios. Второй использует специальный тип функции, называемой манипулятор. Начнем рассмотрение форматирования с использования функций-членов класса ios.
7.4.1 Форматирование с помощью функций-членов класса ios
В заголовочном файле ios.h определены следующие переменные перечисляемого типа:
enum
{
skipws = 0x0001, left = 0x0002, right = 0x0004, internal = 0x0008, dec = 0x0010, oct = 0x0020, hex = 0x0040, showbase = 0x0080, showpoint = 0x0100, uppercase = 0x0200, showpos = 0x0400, scientific = 0x0800, fixed = 0x1000, unitbuf = 0x2000, stdio = 0x4000, boolalpha = 0x8000
};
Определенные таким способом значения переменных перечисляемого типа используются для установки или сброса флагов, управляющих форматированием информации потоков.
Когда установлен флаг skipws, то следующие в начале символы-разделители (пробелы, символы табуляции и новой строки) при выполнении ввода отбрасываются. Когда флаг skipws сброшен, символы-разделители сохраняются.
При установке флага left осуществляется вывод с левым выравниванием. Соответственно при установке флага right вывод осуществляется с правым выравниванием. По умолчанию вывод осуществляется с выравниванием по правому краю.
При установке флага internal поле, отведенное для вывода, заполняется пробелами до знака или буквы. Далее будет показано, как указать ширину поля.
Также по умолчанию численные значения выводятся в десятичной форме. Однако можно переопределить такой способ. Например, установка флага oct задает вывод в восьмеричной форме. Установка флага hex задает вывод в шестнадцатеричной форме. Для возвращения к десятичному выводу следует установить флаг dec.
Установка флага showbase позволяет при выводе указывать числовую базу (десятичную, восьмеричную, шестнадцатеричную).
Установка флага showpoint приводит к выводу десятичной запятой и нулей справа для всех чисел с плавающей запятой вне зависимости, нужно это или нет.
По умолчанию при выводе научных данных, т.е. при использовании экспоненциальной формы записи чисел с плавающей запятой, используется 'е' в нижнем регистре. Также при выводе шестнадцатеричных величин символ 'x' выводится в нижнем регистре. При установке флага uppercase эти символы будут выводиться в верхнем регистре.
Установка флага showpos приводит к тому, что перед положительными числами будет выводиться знак «плюс».
При установке флага scientific числа с плавающей запятой выводятся с использованием научной нотации. При установке флага fixed числа с плавающей запятой выводятся в обычной нотации. По умолчанию, когда установлен флаг fixed, для десятичных цифр выводятся шесть позиций. Когда ни один из флагов не установлен, компилятор выбирает подходящий метод сам.
При установке флага unitbuf при каждой операции вставки данные немедленно заносятся в поток.
При установке флага stdio после каждого вывода данные заносятся в потоки stdout и stderr.
При установке флага boolalpha можно вводить и выводить данные булевого типа.
Флаги хранятся в формате длинных целых. В проекте стандарта ANSI для языка C++ этот тип обозначается как fmtflags, но в MS Visual C++ 7.0 флаги имеют тип long.
Для установки флагов используется функция setf(), чей формат имеет следующий вид:
long setf(long flags);
Эта функция возвращает предыдущее значение флага и присваивает ему значение параметра flags. При этом все остальные флаги остаются неизменными. Например, для того чтобы установить флаг showbase, можно использовать следующую инструкцию:
stream.setf(ios::showbase);
Здесь stream является каким-либо потоком, на который надо оказать воздействие. Например, следующая программа устанавливает флаги showpos и scientific для cout:
#include <iostream.h>
int main()
{
cout.setf(ios::showpos);
cout.setf(ios::scientific);
cout << 123 << " " << 123.23 << " ";
return 0;
}
Данная программа выводит на экран следующие данные:
+123 +1.232300е+02
С помощью операции побитового ИЛИ можно установить одновременно столько флагов, сколько надо. Например, можно изменить программу таким образом, что будет осуществляться только один вызов функции setf() при установке обоих флагов scientific и showpos:
cout.setf(ios::scientific || ios::showpos);
Для отключения флагов надо использовать функцию unsetf(). Она имеет следующий прототип:
long unsetf(long flags);
Функция возвращает значение состояния флага и сбрасывает флаги, определяемые параметром flags.
Иногда бывает полезным знать текущую установку флагов. Можно получить текущие значения флагов, используя функцию flags(), которая имеет следующий прототип:
long flags() const;
Эта функция возвращает текущие значения флагов, ассоциированных с тем потоком, членом которого она является. Используя следующую форму функции flags(), можно установить значения флагов на указанные в параметрах flags() и возвратить предыдущие значения флагов:
long flags(long new_flags);
Для того чтобы познакомиться с работой функций flags() и unsetf(), рассмотрим следующую программу. Она включает в себя функцию, называемую showflags(), которая выводит значения флагов.
#include <iostream.h>
void showflags(long f)
{
for(long i=0x8000; i; i = i >> 1)
if(i & f) cout << “1“;
else cout << "0";
cout << "\n";
}
int main()
{
long f = cout.flags();
showflags(f);
cout.setf(ios::showpos | ios::scientific);
f = cout.flags();
showflags(f);
cout.unsetf(ios::scientific);
f = cout.flags();
showflags(f);
return 0;
}
Исполнение данной программы выдаст следующие данные:
0000000000000001
0000110000000001
0000010000000001
Кроме флага форматирования также можно установить ширину поля потока, символ для заполнения и число цифр после десятичной запятой. Для этого используются следующие функции:
int width(int len);
char fill(char ch);
int precision(int num);
Функция width() возвращает текущую ширину поля потока и устанавливает новое значение, определяемое параметром len. По умолчанию ширина поля изменяется в зависимости от числа символов, необходимых для записи данных. Функция fill() возвращает текущее значение символа для заполнения, которым по умолчанию является пробел, и устанавливает текущий символ заполнения равным ch. Символ заполнения используется для заполнения пустых мест в соответствии с заданной спецификацией ширины поля. Функция precision() возвращает число цифр после десятичной запятой и устанавливает новое значение, задаваемое параметром пит. Ниже представлена программа, демонстрирующая использование этих трех функций:
#include <iostream.h>
int main()
{
cout.setf(ios::showpos | ios::scientific);
cout << 123 << " " << 123.23 << "\n";
cout.precision(2); // две цифры после запятой
cout.width(10); // поле из десяти символов
cout << 123 << " " << 123.23 << "\n";
cout.fill('#'); // заполнение символом #
cout.width(10); // поле из десяти символов
cout << 123 << " " << 123.23;
return 0;
}
Программа выдаст следующий результат:
+123 +1.232300е+02
+123 +1.23е+02
######+123 +1.23е+02
Надо помнить, что флаги одного потока независимы от флагов другого потока. Изменение флагов в одном потоке не влияет на флаги другого.