Reputation: 25748
I am wondering what's the best practice to print out a class (say classA), I have several method
classA ca;
1) Define a debug method and inside this method , print out all its members.
2) Define a str() method, and use cout<< ca.str()
3) Define something like string conversion(I am not sure how yet), and just use cout << ca
Upvotes: 0
Views: 441
Reputation: 1458
clog can be an option if you want to log your steps of a process in some file and review the file later to see what goes wrong. Also you can check state of data members at certain interval by logging them externally in file.
Upvotes: 0
Reputation: 44043
The usual way is to overload operator<<
roughly like this:
std::ostream &operator<<(std::ostream &out, classA const &a) {
// precise formatting depends on your use case. Do the same thing you would
// if you wanted to print a to cout or a file, because that is what this code
// will end up doing.
out << a.some_data() << ", " << a.some_more_data();
return out;
}
If classA
has a limited interface (does not expose all relevant data members), it may be necessary to make the operator<<
a friend
of classA
, such as
class A {
public:
// stuff
friend operator<<(std::ostream &out, classA const &a);
};
// private members can then be accessed
std::ostream &operator<<(std::ostream &out, classA const &a) {
out << a.some_private_member << ", " << a.some_other_private_member;
return out;
}
There is not usually a good reason to prevent read access to private members that you then enable the user to dump out per operator<<
, though, since it would be rather leaky access control.
This then enables you to write
classA a;
std::cout << a;
std::ofstream file("foo.txt");
file << a;
std::ostringstream fmt;
fmt << a;
std::string s = fmt.str();
and so forth.
As a style note: It is possible to write
std::ostream &operator<<(std::ostream &out, classA const &a) {
// precise formatting depends on your use case
return out << a.some_data() << ", " << a.some_more_data();
}
This achieves the same thing as the split return because operator<<
(by convention) returns the same stream object that was passed into it (to enable the chaining of <<
as in std::cout << i << j << k;
).
Style note 2: If there is nothing in classA
that makes it difficult, an upgrade to this technique is to write
template<typename char_type>
std::basic_ostream<char_type> &operator<<(std::basic_ostream<char_type> &out, classA const &a) {
// rest as before. Put it in a header because it is a function template now.
}
This enables you to write classA
objects not only to cout
, cerr
, clog
, ofstream
, ostringstream
etc., but also to their wchar_t
counterparts wcout
, wcerr
, wclog
, wofstream
, and wostringstream
. These are rarely used in practice, but often it will cost you nothing to implement this feature. The trick is that std::ostream
and std::wostream
-- base classes of all these output streams -- are aliases for std::basic_ostream<char>
and std::basic_ostream<wchar_t>
, respectively. That gives us this nice way to handle both (and potentially other) character classes without code duplication.
Upvotes: 3