- •Объектно-ориентированное программирование:
- •Содержание
- •Новый тип данных – класс
- •1.2. Доступность компонентов класса
- •1.3. Конструктор и деструктор
- •1.4.4. Статические члены класса
- •1.4. Компоненты-данные и компоненты-функции
- •1.4.1. Данные – члены класса
- •1.4.2. Функции – члены класса
- •1.4.3. Константные компоненты-функции
- •Указатели на компоненты класса
- •1.5.1. Указатели на компоненты-данные
- •1.5.2.Указатели на компоненты-функции
- •1.6. Указатель this
- •1.7. Друзья классов
- •7.1. Дружественная функция
- •1.7.2. Дружественный класс
- •1.8. Определение классов и методов классов
- •2. Наследование
- •2.1. Определение производного класса
- •2.2. Конструкторы и деструкторы производных классов
- •2.3. Виртуальные функции
- •2.4. Абстрактные классы
- •2.5. Включение объектов
- •2.6. Включение и наследование
- •2.7. Множественное наследование
- •2.8. Локальные и вложенные классы
- •2.9. Пример программы для Microsoft Visual Studio
- •2.10. Упражнения
- •3. Перегрузка операций
- •3.1. Перегрузка унарных операций
- •3.2. Перегрузка бинарных операций
- •3.4. Перегрузка операции вызова функции
- •3.5. Перегрузка операции присваивания
- •3.6. Перегрузка операции new
- •3.7. Перегрузка операции delete.
- •3.8. Основные правила перегрузки операций
- •3.9. Примеры программ
- •4. Шаблоны функций и классов
- •4.1. Шаблоны функций
- •4.2. Шаблоны классов
- •4.3. Компонентные функции параметризованных классов
- •4.4. Примеры программ.
- •5. Обработка исключительных ситуаций
- •5.2. Получение дополнительной информации об исключении
- •5.3. Определение типа исключения
- •5.4. Иерархия исключений
- •5.5. Спецификация функций, обрабатывающих исключения
- •6. Потоковые классы
- •6.1. Библиотека потоковых классов
- •6.3. Стандартные потоки ввода-вывода
- •6.4. Форматирование
- •6.5. Манипуляторы
- •6.6. Ввод-вывод объектов пользовательских классов
- •6.7. Определение пользовательских манипуляторов
- •6.8. Пользовательские манипуляторы с параметрами
- •6.9. Использование макросов для создания манипуляторов
- •6.10. Состояние потока
- •6.11. Неформатированный ввод-вывод
- •6.12. Файловый ввод-вывод
- •7.1. Пространство имен
- •7.2. Динамическая идентификация типов
- •7.3. Безопасное приведение типа
- •8. Стандартная библиотека шаблонов
- •8.1. Введение в stl
- •Парадигму обобщенного программирования можно сформулировать следующим образом:
- •8.2. Итераторы
- •8.3. Классы-контейнеры
- •Iterator find(const key_type&k)
- •Iterator lower_bound(const key_type&k)
- •Iterator upper_bound(const key_type&k)
- •8.4. Контейнер vector
- •8.5. Многомерные массивы
- •8.6. Ассоциативные контейнеры
- •8.7. Объекты-функции
- •8.8. Алгоритмы
- •I. Немодифицирующие операции
- •II. Модифицирующие операции
- •III. Сортировка
- •IV. Работа с множествами
- •V. Минимумы и максимумы
- •VI. Перестановки
6.7. Определение пользовательских манипуляторов
В ситуации, когда нужно повторно определять одну и ту же команду форматирования, пользовательский манипулятор обеспечивает простую запись, нечто вроде макро.
Манипулятор без параметра создается следующим образом. Определяется класс с функцией operator<<(), которая, в свою очередь, вызывает определенные функции форматирования.
Пример 6.7.1.
Определим класс манипулятора
class my_f{};
Перегрузим функцию-оператор operator<< для объекта класса манипулятора. Манипулятор позволит выполнить выравнивание по правому краю. По умолчанию выравнивание по левому краю.
ostream& operator<<(ostream& out,my_f)
{
out.width(12);
out.fill(‘# ’);
cout.setf(ios:: right,ios::adjustfield);
return out;
}
void main()
{my_f MF;
cout<<52.3456<<endl<<MF<<52.3456<<endl;
}
Синтаксис языка разрешает параметры без имен, если последние не используются в теле функции. В стандарте языка С++ отмечается, что использование специального параметра без имени «полезно для резервирования места в списке параметров». В дальнейшем этот параметр может быть введен в функции без изменения интерфейса, т.е. без изменения вызывающей программы. Такая возможность бывает удобной при развитии уже существующей программы за счет изменения входящих в нее функций.
Так как манипулятор–это функция, принимающая и возвращающая ссылку на поток, то лучше определить манипулятор MF так:
Пример 6.7.2..
ostream& MF(ostream& out)
{
return out<<resetiosflags(ios::right)<<setiosflags(ios::right);
}
Эти два примера показывают два различных подхода к созданию пользовательских манипуляторов. В первом случае мы, по сути, не создавали манипулятор, а перегрузили операцию >> для класса my_f. Во втором случае мы создали собственно функцию-манипулятор. Вызов её обеспечивает перегруженная операция << в классе ostream.
6.8. Пользовательские манипуляторы с параметрами
Пусть, например, необходимо создать новый манипулятор с параметрами wp(n,m), где n – ширина поля вывода, m – точность.
Пример 6.8.1.
//Класс манипулятора
class my_manip {
int n,m;
ostream&(*f)(ostream&,int,int);
public:
//конструктор
my_manip(ostream&(*F)(ostream&,int,int),int N, int M): f(F),n(N),m(M){}
//здесь f,F – указатели на функцию
friend ostream& operator<<(ostream& s,my_manip& my)
{return my.f(s,my.n,my.m);}
};
ostream& wi_pre(ostream& s,int n,int m)
{s.width(n);
s.flags(ios::fixed);
s.precision(m);
return s;
}
my_manip wp(int n,int m)
{return my_manip(wi_pre,n,m);
}
void main()
{cout<<52.3456<<endl<<wp(10,5)<<52.3456<<endl;
}
Рассмотрим этот пример подробнее. Конструктор класса my_manip имеет первый аргумент ostream&(*F)(ostream&,int,int). Здесь F – указатель на функцию, имеющую три аргумента типов ostream&, int и int и возвращающую ссылку на класс ostream. Теперь конкретную функцию можно задавать для класса my_manip через указатель при конструировании объекта этого класса. Мы задали функцию wi_pre. При выполнении cout<<wp(10,5) фактически вызывается следующая функция:
osteram& operator<<(cout,wp(10,5))
{return wi_pre(cout,10,5);}
Действительно, функция wp возвращает объект класса my_manip с передачей в его конструктор параметров: wi_pre, n и m.
Сформулируем последовательность создания пользовательского манипулятора с параметрами.
1. Определить класс (my_manip) с полями: параметры манипулятора + поле – указатель на функцию типа
ostream& (*f)(ostream&,<параметры манипулятора>);
2. Определить конструктор этого класса (my_manip) с инициализацией полей.
3. Определить в этом классе дружественную функцию – operator<<. Эта функция в качестве правого аргумента принимает объект класса (my_manip), левого аргумента (операнда) поток ostream и возвращает поток ostream как результат выполнения функции f.
4. Определить функцию типа *f(wi_pre), принимающую поток и параметры манипулятора и возвращающую поток. Эта функция выполняет форматирование.
5. Определить собственно манипулятор (wp) как функцию, принимающую параметры манипулятора и возвращающую объект my_manip, поле f которого содержит указатель на функцию wi_pre.
При выполнении манипулятора cout<<wp(10,5); вызывается функция wp, которая создает и возвращает объект my_manip, в поле f которого содержится указатель на функцию wi_pre.
Затем вызывается функция operator<<, которой передается объект my_manip, созданный функцией wp.
Наконец, функция operator<< выполняет функцию *f, т.е. wi_pre, и возвращает поток.
