После этого определите версию 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.
Пример 10.7. Чтение данных из потока в объект
#include <iostream>
#include <istream>
#include <fstream>
#include <string>
using namespace std;
class Employee {
friend ostream& operator<< // Они должны быть друзьями,
(ostream& out, const Employee& emp); // чтобы получить доступ к
friend istream& operator>> // неоткрытым членам
(istream& in, Employee& emp);
public:
Employee() {}
~Employee() {}
void setFirstName(const string& name) {firstName_ = name;}
void setLastName(const string& name) {lastName_ = name;}
private:
string firstName_;
string lastName_;
};
// Передать в поток объект Employee...
ostream& operator<<(ostream& out, const Employee& emp) {
out << emp.firstName_ << endl;
out << emp.lastName_ << endl;
return(out);
}
// Считать из потока объект Employee
istream& operator>>(istream& in, Employee& emp) {
in >> emp.firstName_;
in >> emp.lastName_;
return(in);
}
int main() {
Employee emp;
string first = "William";
string last = "Shatner";
emp.setFirstName(first);
emp.setLastName(last);
ofstream out("tmp\\emp.txt");
if (!out) {
cerr << "Unable to open output file.\n";
exit(EXIT_FAILURE);
}
out << emp; // Записать Emp в файл
out.close();
ifstream in("tmp\\emp.txt");
if (!in) {
cerr << "Unable to open input file.\n";
exit(EXIT_FAILURE);
}
Employee emp2;
in >> emp2; // Считать файл в пустой объект
in.close();
cout << emp2;
}
При создании класса, считываемого из потока, выполняемые этапы почта совпадают с этапами записи объекта в поток (только они имеют обратный характер) Если вы еще не прочитали рецепт 10.4, это необходимо сделать сейчас, чтобы был понятен пример 10.7.
Во-первых, вам необходимо объявить operator>> как дружественный для вашего целевого класса, однако в данном случае вам нужно, чтобы он использовал поток istream, а не ostream. Затем определите оператор operator>> (вместо operator<<) для прямого чтения значений из потока в каждую переменную-член вашего класса. Завершив чтение данных, возвратите входной поток.
Рецепт 10.4.
10.6. Получение информации о файле
Требуется получить информацию о файле, например о его размере, номере устройства, времени последнего изменения и т.п.
Используйте вызов системной C-функции stat, определенной в <sys/stat.h>. См. Пример 10.8, где показано типичное применение stat для вывода на печать некоторых атрибутов файла.
Пример 10.8. Получение информации о файле
#include <iostream>
#include <ctime>
#include <sys/types.h>
#include <sys/stat.h>