- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
C++. Сборник рецептов - Д. Стефенс
Шрифт:
Интервал:
Закладка:
ws(os); // Передать поток объекту ws
return(os); // для выполнения реальной работы
}
Это решает проблему, но не в общем виде. Вам не захочется писать класс типа WidthSetter для каждого вашего манипулятора, принимающего аргумент (возможно, вы это и делаете, но были бы не против поступить по-другому), поэтому лучше использовать шаблоны и указатели функций для получения привлекательной, обобщенной инфраструктуры, на базе которой вы можете создавать любое количество манипуляторов. Пример 10.5 содержит класс ManipInfra и версию operator<<, использующую аргументы шаблона для работы с различными типами символов, которые может содержать поток, и с различными типами аргументов, которые могут быть использованы манипулятором потока.
Пример 10.5. Инфраструктура манипуляторов
#include <iostream>
#include <string>
using namespace std;
// ManipInfra - это небольшой промежуточный класс, который помогает
// создавать специальные манипуляторы с аргументами. Вызывайте его
// конструктор с предоставлением указателя функции и значения из основной
// функции манипулятора
// Указатель функции должен ссылаться на вспомогательную функцию, которая
// делает реальную работу. См. примеры ниже
template<typename T, typename C>
class ManipInfra {
public:
ManipInfra(basic_ostream<C>& (*pFun) (basic_ostream<C>&, T), T val) :
manipFun_(pFun), val_(val) {}
void operator()(basic_ostream<C>& os) const {
manipFun_(os, val_);
} // Вызовите функцию, задавая ее указатель и
private: // передавая ей поток и значение
T val_;
basic_ostream<С>& (*manipFun_) (basic_ostream<C>&, T);
};
template<typename T, typename C>
basic_ostream<C>& operator<<(basic_ostream<C>& os,
const ManipInfra<T, C>& manip) {
manip(os);
return(os);
}
// Вспомогательная функция, которая вызывается в итоге в классе ManipInfra
ostream& setTheWidth(ostream& os, int n) {
os.width(n);
return(os);
}
// Собственно функция манипулятора. Именно она используется в клиентском
// программном коде
ManipInfra<int, char> setWidth(int n) {
return(ManipInfra<int, char>(setTheWidth, n));
}
// Ещё одна вспомогательная функция, которая принимает аргумент типа char
ostream& setTheFillChar(ostream& os, char с) {
os.fill(c);
return(os);
}
ManipInfra<char, char> setFill(char c) {
return(ManipInfra<char, char>(setTheFillChar, c));
}
int main() {
cout << setFill('-')
<< setWidth(10) << right << "Proustn";
}
Если последовательность событий при работе этого класса все же остается неясной, я советую прогнать пример 10.5 через отладчик. Увидев его реальную работу, вы все поймете.
10.4. Создание класса, записываемого в поток
ПроблемаТребуется записать класс в поток для последующего его чтения человеком или с целью его хранения в постоянной памяти, т.е. для его сериализации.
РешениеПерегрузите operator<< для записи в поток соответствующих данных-членов. В примере 10.6 показано, как это можно сделать.
Пример 10.6. Запись объектов в поток
#include <iostream>
#include <string>
using namespace std;
class Employer {
friend ostream& operator<< // Он должен быть другом для
(ostream& out, const Employer& empr); // получения доступа к неоткрытым
public: // членам
Employer() {}
~Employer() {}
void setName(const string& name) {name_ = name;}
private:
string name_;
};
class Employee {
friend ostream& operator<< (ostream& out, const Employee& obj);
public:
Employee() : empr_(NULL) {}
~Employee() {if (empr_) delete empr_;}
void setFirstName(const string& name) {firstName_ = name;}
void setLasttName(const string& name) {lastName_ = name;}
void setEmployer(Employer& empr) {empr_ = &empr;}
const Employer* getEmployer() const {return(empr_);}
private:
string firstName_;
string lastName_;
Employer* empr_;
};
// Обеспечить передачу в поток объектов
Employer... ostream& operator<<(ostream& out, const Employer& empr) {
out << empr.name_ << endl; return(out);
}
// Обеспечить передачу в поток объектов Employee...
ostream& operator<<(ostream& out, const Employee& emp) {
out << emp.firstName_ << endl;
out << emp.lastName_ << endl;
if (emp.empr_) out << *emp.empr_ << endl;
return(out);
}
int main() {
Employee emp;
string first = "William";
string last = "Shatner";
Employer empr;
string name = "Enterprise";
empr.setName(name);
emp.setFirstName(first);
emp.setLastName(last);
emp.setEmployer(empr);
cout << emp; // Запись в поток
}
ОбсуждениеПрежде всего, необходимо объявить оператор operator<< другом (friend) класса, который вы хотите записывать в поток. Вы должны использовать operator<<, а не функцию-член типа writeToStream(ostream& os), потому что этот оператор принято использовать в стандартной библиотеке для записи любых объектов в поток. Вам придется объявить его другом, потому что в большинстве случаев потребуется записывать в поток закрытые члены, а не являющиеся друзьями функции не смогут получить доступ к ним.
После этого определите версию operator<<, которая работает с ostream или wostream (которые определены в <ostream>) и вашим классом, который вы уже объявили с ключевым словом friend. Здесь вы должны решить, какие данные-члены должны записываться в поток. Обычно потребуется записывать в поток все данные, как это я делал в примере 10.6.
out << emp.firstName_ << endl;
out << emp.lastName_ << endl;
В примере 10.6 я записал в поток объект, на который ссылается указатель empr_, вызывая для него оператор operator<<.
if (emp.empr_)
out << *emp.empr << endl;
Я могу так делать, потому что empr_ указывает на объект класса Employer, а для него, как и для Employee, я определил оператор operator<<.
После записи в поток членов вашего класса ваш оператор operator<< должен возвратить переданный ему поток. Это необходимо делать в любой перегрузке operator<<, тогда она может успешно использоваться, как в следующем примере.
cout << "Here's my object. " << myObj << 'n';
Описанный мною подход достаточно прост, и если вы собираетесь записывать класс с целью его дальнейшего восприятия человеком, он будет хорошо работать, но это только частичное решение проблемы. Если вы записываете объект в поток, это обычно делается по одной из двух причин. Либо этот поток направляется куда-то, где он будет прочитан человеком (cout, окно терминала, файл журнала и т.п.), либо поток записывается на носитель временной или постоянной памяти (stringstream, сетевое соединение, файл и т.д.) и вы планируете восстановить в будущем объект из потока. Если вам требуется воссоздать объект из потока (тема рецепта 10.5), необходимо тщательно продумать взаимосвязи вашего класса.
Сериализация трудно реализуется для любых классов, не считая тривиальных. Если в вашем классе имеются ссылки или указатели на другие классы, что характерно для большинства нетривиальных классов, вам придется учесть потенциальную возможность наличия циклических ссылок, обработать их должным образом при записи в поток объектов и правильно реконструировать ссылки при считывании объектов. Если вам приходится строить что-то «с чистого листа», необходимо учесть эти особенности проектирования, однако если вы можете использовать внешнюю библиотеку, вам следует воспользоваться библиотекой Boost Serialization, которая обеспечивает переносимый фреймворк сериализации объектов.
Смотри такжеРецепт 10.5.
10.5. Создание класса, считываемого из потока
ПроблемаВ поток записан объект некоторого класса и теперь требуется считать эти данные из потока и использовать их для инициализации объекта того же самого класса.
РешениеИспользуйте operator>> для чтения данных из потока в ваш класс для заполнения значений данных-членов; это просто является обратной задачей по отношению к тому, что сделано в примере 10.6. Реализация приводится в примере 10.7.

