Д. Стефенс - C++. Сборник рецептов
- Название:C++. Сборник рецептов
- Автор:
- Жанр:
- Издательство:КУДИЦ-ПРЕСС
- Год:2007
- Город:Москва
- ISBN:5-91136-030-6
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Д. Стефенс - C++. Сборник рецептов краткое содержание
Данная книга написана экспертами по C++ и содержит готовые рецепты решения каждодневных задач для программистов на С++. Один из авторов является создателем библиотеки Boost Iostreams и нескольких других библиотек C++ с открытым исходным кодом. В книге затрагивается множество тем, вот лишь некоторые из них: работа с датой и временем; потоковый ввод/вывод; обработка исключений; работа с классами и объектами; сборка приложений; синтаксический анализ XML-документов; программирование математических задач. Читатель сможет использовать готовые решения, а сэкономленное время и усилия направить на решение конкретных задач.
C++. Сборник рецептов - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Требуется иметь манипулятор потока, который делает что-нибудь такое, что не могут делать стандартные манипуляторы. Или вам нужен такой один манипулятор, который устанавливает несколько флагов потока, и вам не приходится вызывать несколько манипуляторов всякий раз, когда необходимо установить конкретный формат вывода.
Чтобы создать манипулятор, который не имеет аргументов (типа left
), напишите функцию, которая принимает в качестве параметра ios_base
и устанавливает для него флаги потока. Если вам нужен манипулятор с аргументом, см. приводимое ниже обсуждение. Пример 10.4 показывает возможный вид манипулятора без аргументов.
Пример 10.4. Простой манипулятор потока
#include
#include
#include
using namespace std;
// вывести числа с плавающей точкой в обычном виде
inline ios_base& floatnormal(ios_base& io) {
io.setf(0, ios_base::floatfield);
return(io);
}
int main() {
ios_base::fmtflags flags = // Сохранить старые флаги
cout.flags();
double pi = 22.0/7.0;
cout << pi = " << scientific // Научный режим
<< pi * 1000 << '\n';
cout << "pi = " << floatnormal << pi << '\n';
cout.flags(flags);
}
Существует два вида манипуляторов: с аргументами и без аргументов. Манипуляторы без аргументов пишутся просто. Вам требуется только написать функцию, которая принимает в качестве параметра поток, выполнить с ним какие-то действия (установить флаги или изменить установочные параметры) и возвратить его. Сложнее написать манипулятор, который имеет один или несколько аргументов, потому что потребуется создавать дополнительные классы и функции, которые работают «за кулисами». Поскольку манипуляторы без аргументов более простые, начнем с них.
Прочитав рецепт 10.1, вероятно, вы поняли, что существует три формата вывода чисел с плавающей точкой и только два манипулятора для выбора формата. Для используемого по умолчанию формата не предусмотрен манипулятор; вам придется соответствующим образом установить флаг для потока, чтобы вернуться к стандартному формату:
myiostr.setf(0, ios_base::float field);
Но для удобства вы можете добавить свой собственный манипулятор, делающий то же самое. Именно это сделано в примере 10.4. Манипулятор floatnormal
устанавливает соответствующий флаг потока для вывода чисел с плавающей точкой в стандартном формате.
Компилятор знает, что делать с вашей новой функцией, потому что в стандартной библиотеке определен следующий оператор для basic_ostream
( basic_ostream
— имя шаблона класса, инстанцируемого в классах ostream
и wostream
).
basic_ostream& operator<<
(basic_ostream& (* pf)basic_ostream&))
Здесь pf
— это указатель функции, которая принимает в аргументе ссылку на basic_ostream
и возвращает ссылку на basic_ostream
. Этот оператор просто обеспечивает вызов вашей функции, которая принимает в качестве аргумента текущий поток.
Манипуляторы с аргументами более сложные. Чтобы понять причину этого, рассмотрим работу манипулятора без аргументов. Пусть используется, например, следующий манипулятор
myostream << myManip << "foo";
Вы задаете его без скобок, поэтому его имя в действительности заменяется адресом функции вашего манипулятора. В действительности operator<<
вызывает функцию манипулятора и передает ей поток, чтобы манипулятор мог выполнить свою работу.
Для сравнения представим, что у вас имеется манипулятор, который принимает числовой аргумент, так что в идеале вы могли бы его использовать следующим образом.
myostream << myFancyManip(17) << "apple";
Как это будет работать? Если вы считаете, что myFancyManip
является функцией, принимающей целочисленный аргумент, то возникает проблема: как передать поток в функцию без включения его в параметры и явного его использования? Вы могли бы написать так.
myostream << myFancyManip(17, myostream) << "apple";
Но это выглядит непривлекательно и избыточно. Одним из удобств манипуляторов является то, что их можно просто добавлять в строку с группой операторов operator<<
, и они хорошо воспринимаются и используются.
Решение состоит в том, чтобы заставить компилятор пойти окольным путем. Вместо того чтобы operator<<
вызывал функцию вашего манипулятора для потока, вам надо просто создать временный объект, который возвращает нечто такое, что может использовать operator<<
.
Во-первых, вам необходимо определить временный класс, который делал бы всю работу. Для простоты предположим, что вам требуется написать манипулятор с именем setWidth
, который делает то же самое, что и setw
. Временная структура, которую вам необходимо построить, будет выглядеть примерно так.
class WidthSetter {
public:
WidthSetter(int n) : width_(n) {}
void operator()(ostream& os) const {os.width(width_);}
private:
int width_;
};
Этот класс содержит простую функцию. Предусмотрите в ней целочисленный аргумент и, когда operator()
вызывается с аргументом потока, установите ширину для потока в значение, в какое она была установлена при инициализации этого объекта. В результате мы получим WidthSetter
, сконструированный одной функцией и используемый другой. Ваш манипулятор конструирует эту функцию, и это будет выглядеть следующим образом.
WidthSetter setWidth(int n) {
return(WidthSetter(n)); // Возвращает инициализированный объект
}
Эта функция всего лишь возвращает объект WidthSetter
, инициализированный целым значением. Этот манипулятор вы будете использовать в строке операторов operator<<
следующим образом.
myostream << setWidth(20) << "banana";
Но этого недостаточно, потому что setWidth
просто возвращает объект WidthSetter
; operator<<
не будет знать, что с ним делать. Вам придется перегрузить operator<<
, чтобы он знал, как управлять объектом WidthSetter
:
ostream& operator<<(ostream& os, const WidthSetter& ws) {
ws(os); // Передать поток объекту ws
return(os); // для выполнения реальной работы
}
Это решает проблему, но не в общем виде. Вам не захочется писать класс типа WidthSetter
для каждого вашего манипулятора, принимающего аргумент (возможно, вы это и делаете, но были бы не против поступить по-другому), поэтому лучше использовать шаблоны и указатели функций для получения привлекательной, обобщенной инфраструктуры, на базе которой вы можете создавать любое количество манипуляторов. Пример 10.5 содержит класс ManipInfra
и версию operator<<
, использующую аргументы шаблона для работы с различными типами символов, которые может содержать поток, и с различными типами аргументов, которые могут быть использованы манипулятором потока.
Интервал:
Закладка: