Прежде всего, необходимо объявить оператор operator<<
другом (friend
) класса, который вы хотите записывать в поток. Вы должны использовать operator<<
, а не функцию-член типа writeToStream(ostream& os)
, потому что этот оператор принято использовать в стандартной библиотеке для записи любых объектов в поток. Вам придется объявить его другом, потому что в большинстве случаев потребуется записывать в поток закрытые члены, а не являющиеся друзьями функции не смогут получить доступ к ним.
После этого определите версию operator<<
, которая работает с ostream
или wostream
(которые определены в
) и вашим классом, который вы уже объявили с ключевым словом 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.
#include
#include
#include
#include
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);
}