- •Глава 6 посвящена понятию производных классов, которое позволяет строить
- •Раздел 3.4 главы 2. Для обозначения справочного руководства применяется
- •1991 Г.Г. (такие как множественное наследование, статические функции-члены
- •1.1 Введение
- •1.2 Парадигмы программирования
- •1.2.1 Процедурное программирование
- •1.2.5 Объектно-ориентированное программирование
- •1.5 Поддержка объектно-ориентированного программирования
- •1.5.1 Механизм вызова
- •1.5.2 Проверка типа
- •1.5.3 Множественное наследование
- •1.6 Пределы совершенства
- •2.2 Имена
- •2.3.2 Неявное преобразование типа
- •2.4 Литералы
- •2.4.4 Строки
- •2.6. Экономия памяти
- •2.6.1 Поля
- •3.1.1 Анализатор
- •3.1.2 Функция ввода
- •3.2 Сводка операций
- •3.2.3 Инкремент и декремент
- •3.2.5 Преобразование типа
- •3.2.6 Свободная память
- •3.3.2 Оператор goto
- •4.1 Введение
- •4.3.1 Единственный заголовочный файл
- •4.3.2 Множественные заголовочные файлы
- •4.4 Связывание с программами на других языках
- •4.6.3 Передача параметров
- •5.1 Введение и краткий обзор
- •5.3.1 Альтернативные реализации
- •5.3.2 Законченный пример класса
- •Vector и matrix, мы могли бы обойтись без контроля индекса при
- •5.4.5 Указатели на члены
- •5.4.6 Структуры и объединения
- •5.5.3 Свободная память
- •5.5.5 Массивы объектов класса
- •6.1 Введение и краткий обзор
- •6.2.3 Иерархия классов
- •6.2.4 Поля типа
- •6.4.1 Монитор экрана
- •6.5 Множественное наследование
- •7.1 Введение
- •7.3 Пользовательские операции преобразования типа
- •7.3.2 Операции преобразования
- •7.3.3 Неоднозначности
- •7.5 Большие объекты
- •Void f2(t a) // вариант с контролем
- •Void f3(t a) // вариант с контролем
- •Inv() обращает саму матрицу m, а не возвращает новую, обратную m,
- •7.13 Предостережения
- •8.1 Введение
- •8.4.4 Неявная передача операций
- •8.4.5 Введение операций с помощью параметров шаблонного класса
- •8.7.1 Задание реализации с помощью параметров шаблона
- •9.1 Обработка ошибок
- •9.1.2 Другие точки зрения на особые ситуации
- •9.3.2 Производные особые ситуации
- •9.4.2 Предостережения
- •9.4.3 Исчерпание ресурса
- •9.4.4 Особые ситуации и конструкторы
- •9.5 Особые ситуации могут не быть ошибками
- •10.1 Введение
- •10.2 Вывод
- •10.2.1 Вывод встроенных типов
- •10.4.1.2 Поля вывода
- •10.4.1.4 Вывод целых
- •Istream - шаблон типа smanip, а smanip - двойник для ioss.
- •10.5.1 Закрытие потоков
- •10.5.2 Строковые потоки
- •X Целый параметр выдается в шестнадцатеричной записи;
- •11.1 Введение
- •11.2 Цели и средства
- •11.3 Процесс развития
- •11.3.1 Цикл развития
- •11.3.2 Цели проектирования
- •11.3.3 Шаги проектирования
- •11.3.3.1 Шаг 1: определение классов
- •11.3.3.2 Шаг 2: определение набора операций
- •11.3.3.3 Шаг 3: указание зависимостей
- •11.3.3.4 Шаг 4: определение интерфейсов
- •11.3.3.5 Перестройка иерархии классов
- •11.3.3.6 Использование моделей
- •11.3.4 Эксперимент и анализ
- •11.3.5 Тестирование
- •11.3.6 Сопровождение
- •11.3.7 Эффективность
- •11.4 Управление проектом
- •11.4.1 Повторное использование
- •11.4.2 Размер
- •11.4.3 Человеческий фактор
- •11.5 Свод правил
- •11.6 Список литературы с комментариями
- •12.1 Проектирование и язык программирования.
- •12.1.1 Игнорирование классов
- •12.1.2 Игнорирование наследования
- •12.1.3 Игнорирование статического контроля типов
- •12.1.4 Гибридный проект
- •12.2 Классы
- •12.2.1 Что представляют классы?
- •12.2.2 Иерархии классов
- •12.2.3 Зависимости в рамках иерархии классов.
- •Vertical_scrollbar или с помощью одного типа scrollbar, который
- •12.2.6 Отношения использования
- •12.2.7 Отношения внутри класса
- •12.3 Компоненты
- •12.4 Интерфейсы и реализации
- •12.5 Свод правил
- •13.1 Введение
- •13.2 Конкретные типы
- •13.4 Узловые классы
- •1, 2, 6 И 7. Класс, который не удовлетворяет условию 6, походит
- •13.5.1 Информация о типе
- •13.6 Обширный интерфейс
- •13.7 Каркас области приложения
- •13.8 Интерфейсные классы
- •13.10 Управление памятью
10.4.1.4 Вывод целых
Прием задания нового значения множества флагов с помощью операции | и
функций flags() и setf() работает только тогда, когда один бит определяет
значение флага. Не такая ситуация при задании системы счисления целых
или вида выдачи вещественных. Здесь значение, определяющее вид выдачи,
нельзя задать одним битом или комбинацией отдельных битов.
Решение, принятое в <iostream.h>, сводится к использованию
версии функции setf(), работающей со вторым "псевдопараметром", который
показывает какой именно флаг мы хотим добавить к новому значению.
Поэтому обращения
cout.setf(ios::oct,ios::basefield); // восьмеричное
cout.setf(ios::dec,ios::basefield); // десятичное
cout.setf(ios::hex,ios::basefield); // шестнадцатеричное
установят систему счисления, не затрагивая других компонентов состояния
потока. Если система счисления установлена, она используется до явной
переустановки, поэтому
cout << 1234 << ' '; // десятичное по умолчанию
cout << 1234 << ' ';
cout.setf(ios::oct,ios::basefield); // восьмеричное
cout << 1234 << ' ';
cout << 1234 << ' ';
cout.setf(ios::hex,ios::basefield); // шестнадцатеричное
cout << 1234 << ' ';
cout << 1234 << ' ';
напечатает
1234 1234 2322 2322 4d2 4d2
Если появится необходимость указывать систему счисления для каждого
выдаваемого числа, следует установить флаг showbase. Поэтому, добавив
перед приведенными выше обращениями
cout.setf(ios::showbase);
мы получим
1234 1234 02322 02322 0x4d2 0x4d2
Стандартные манипуляторы, приведенные в $$10.4.2.1, предлагают более
элегантный способ определения системы счисления при выводе целых.
10.4.1.5 Выравнивание полей
С помощью обращений к setf() можно управлять расположением символов
в пределах поля:
cout.setf(ios::left,ios::adjustfield); // влево
cout.setf(ios::right,ios::adjustfield); // вправо
cout.setf(ios::internal,ios::adjustfield); // внутреннее
Будет установлено выравнивание в поле вывода, определяемом функцией
ios::width(), причем не затрагивая других компонентов состояния потока.
Выравнивание можно задать следующим образом:
cout.width(4);
cout << '(' << -12 << ")\n";
cout.width(4);
cout.setf(ios::left,ios::adjustfield);
cout << '(' << -12 << ")\n";
cout.width(4);
cout.setf(ios::internal,ios::adjustfield);
cout << '(' << -12 << "\n";
что выдаст
( -12)
(-12 )
(- 12)
Если установлен флаг выравнивания internal (внутренний), то символы
добавляются между знаком и величиной. Как видно, стандартным является
выравнивание вправо.
10.4.1.6 Вывод плавающих чисел.
Вывод вещественных величин также управляется с помощью функций,
работающих с состоянием потока. В частности, обращения:
cout.setf(ios::scientific,ios::floatfield);
cout.setf(ios::fixed,ios::floatfield);
cout.setf(0,ios::floatfield); // вернуться к стандартному
установят вид печати вещественных чисел без изменения других
компонентов состояния потока.
Например:
cout << 1234.56789 << '\n';
cout.setf(ios::scientific,ios::floatfield);
cout << 1234.56789 << '\n';
cout.setf(ios::fixed,ios::floatfield);
cout << 1234.56789 << '\n';
напечатает
1234.57
1.234568e+03
1234.567890
После точки печатается n цифр, как задается в обращении
cout.precision(n)
По умолчанию n равно 6. Вызов функции precision влияет на все операции
ввода-вывода с вещественными до следующего обращения к precision,
поэтому
cout.precision(8);
cout << 1234.56789 << '\n';
cout << 1234.56789 << '\n';
cout.precision(4);
cout << 1234.56789 << '\n';
cout << 1234.56789 << '\n';
выдаст
1234.5679
1234.5679
1235
1235
Заметьте, что происходит округление, а не отбрасывание дробной части.
Стандартные манипуляторы, введенные в $$10.4.2.1, предлагают
более элегантный способ задания формата вывода вещественных.
10.4.2 Манипуляторы
К ним относятся разнообразные операции, которые приходится
применять сразу перед или сразу после операции ввода-вывода. Например:
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 дважды в
подходящее время:
cout << x << flush << y << flush;
Похожие определения существуют и для класса istream:
istream& ws(istream& is ) { return is.eatwhite(); }
class istream : public virtual ios {
// ...
public:
istream& operator>>(istream&, istream& (*) (istream&));
// ...
};
поэтому в строке
cin >> ws >> x;
действительно обобщенные пробелы будут убраны до попытки чтения в x.
Однако, поскольку по умолчанию для операции >> пробелы "съедаются" и
так, данное применение ws() избыточно.
Находят применение и манипуляторы с параметрами. Например,
может появиться желание с помощью
cout << setprecision(4) << angle;
напечатать значение вещественной переменной angle с точностью до
четырех знаков после точки.
Для этого нужно уметь вызывать функцию, которая установит
значение переменной, управляющей в потоке точностью вещественных.
Это достигается, если определить setprecision(4) как объект, который
можно "выводить" с помощью operator<<():
class Omanip_int {
int i;
ostream& (*f) (ostream&,int);
public:
Omanip_int(ostream& (*ff) (ostream&,int), int ii)
: f(ff), i(ii) { }
friend ostream& operator<<(ostream& os, Omanip& m)
{ return m.f(os,m.i); }
};
Конструктор Omanip_int хранит свои аргументы в i и f, а с помощью
operator<< вызывается f() с параметром i. Часто объекты таких классов
называют объект-функция. Чтобы результат строки
cout << setprecision(4) << angle
был таким, как мы хотели, необходимо чтобы обращение setprecision(4)
создавало безымянный объект класса Omanip_int, содержащий значение 4
и указатель на функцию, которая устанавливает в потоке ostream значение
переменной, задающей точность вещественных:
ostream& _set_precision(ostream&,int);
Omanip_int setprecision(int i)
{
return Omanip_int(&_set_precision,i);
}
Учитывая сделанные определения, operator<<() приведет к вызову
precision(i).
Утомительно определять классы наподобие Omanip_int для всех
типов аргументов, поэтому определим шаблон типа:
template<class T> class OMANIP {
T i;
ostream& (*f) (ostream&,T);
public:
OMANIP(ostream (*ff) (ostream&,T), T ii)
: f(ff), i(ii) { }
friend ostream& operator<<(ostream& os, OMANIP& m)
{ return m.f(os,m.i) }
};
С помощью OMANIP пример с установкой точности можно сократить так:
ostream& precision(ostream& os,int)
{
os.precision(i);
return os;
}
OMANIP<int> setprecision(int i)
{
return OMANIP<int>(&precision,i);
}
В файле <iomanip.h> можно найти шаблон типа OMANIP, его двойник для
