Problem
You have to write a class to an output stream, either for human readability or persistent storage, i.e., serialization.
Solution
Overload operator<< to write the appropriate data members to the stream. Example 10-6 shows how.
Example 10-6. Writing objects to a stream
#include #include using namespace std; class Employer { friend ostream& operator<< // This has to be a friend (ostream& out, const Employer& empr); // so it can access non- public: // public members 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 setLastName(const string& name) {lastName_ = name;} void setEmployer(Employer& empr) {empr_ = &empr;} const Employer* getEmployer( ) const {return(empr_);} private: string firstName_; string lastName_; Employer* empr_; }; // Allow us to send Employer objects to an ostream... ostream& operator<<(ostream& out, const Employer& empr) { out << empr.name_ << endl; return(out); } // Allow us to send Employee objects to an ostream... 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; // Write to the stream }
Discussion
The first thing you need to do is declare operator<< as a friend of the class you want to write to a stream. You should use operator<< instead of a member function like writeToStream(ostream& os) because the left-shift operator is the convention for writing everything else in the standard library to a stream. You need to declare it as a friend because, in most cases, you want to write private members to the stream, and non-friend functions can't access them.
After that, define the version of operator<< that operates on an ostream or wostream (defined in ) and your class that you have already declared as a friend. This is where you have to decide which data members should be written to the stream. Typically, you will want to write all data members to the stream, as I did in Example 10-6:
out << emp.firstName_ << endl; out << emp.lastName_ << endl;
In Example 10-6, I wrote the object pointed to by empr_ by invoking operator<< on it:
if (emp.empr_) out << *emp.empr_ << endl;
I can do this because empr_ points to an object of the Employer class, and, like Employee, I have defined operator<< for it.
When you are done writing your class's members to the stream, your operator<< must return the stream it was passed. You need to do this whenever you overload operator<<, so it can be used in succession, like this:
cout << "Here's my object: " << myObj << ' ';
The approach I give is simple, and when you want to be able to write a class to an output stream for human consumption, it will work just fine, but that's only part of the story. If you are writing an object to a stream, it's usually for one of two reasons. Either that stream goes somewhere that will be read by a person (cout, console window, a log file, etc.), or the stream is a temporary or persistent storage medium (a stringstream, a network connection, a file, etc.), and you plan on reassembling the object from that stream in the future. If you need to recreate the object from a stream (the subject of Recipe 10.5), you need to think carefully about your class relationships.
Implementing serialization for anything other than trivial classes is hard work. If your class references (via pointer or reference) other classesas most nontrivial classes doyou have to accommodate the potential for circular references in a meaningful way when writing out objects, and you have to reconstruct references correctly when reading them in. If you have to build something from scratch, then you'll have to handle these design considerations, but if you can use an external library, you should try the Boost Serialization library, which provides a framework for serializing objects in a portable way.
See Also
Recipe 10.5
Building C++ Applications
Code Organization
Numbers
Strings and Text
Dates and Times
Managing Data with Containers
Algorithms
Classes
Exceptions and Safety
Streams and Files
Science and Mathematics
Multithreading
Internationalization
XML
Miscellaneous
Index