i need someone who explain me these lines of code part by part and i need some help in using "ostream" with simple examples. thank you :).
inline std::ostream& operator<<(std::ostream& os, const Telegram& t)
{
os << "time: " << t.DispatchTime << " Sender: " << t.Sender
<< " Receiver: " << t.Receiver << " Msg: " << t.Msg;
return os;
}
UPDATE 1: when i use this function it doesnt compile and the error says:
std::ostream& class:开发者_如何学运维:operator<<(std::ostream& os, const Telegram& t) must take exactly one argument
These line are simply adding the ability to handle Telegram objects to the standard output stream class.
When you add a new class and you want output streams like cout
to intelligently handle them, you need to add a new <<
operator method which has the new object type as the second argument.
What the code above is doing is exactly that. When you later execute the statements:
Telegram tg("Bob", "Hello, how are you?");
cout << tg;
that function in your question will be called with the stream as the first argument and your tg
object as the second argument and it will then be able to output the data in a format suitable to the class.
This was actually one of the early C++ things I had trouble getting my head around. Although the class is supposed to be self-contained, you're actually adding something to a different class to handle the output. Once you understand why this is happening (because it's the ostream
class that is responsible for outputting things rather than your own class), it will hopefully make sense.
Hopefully making it clearer with a simpler example:
1 inline std::ostream& operator<<(std::ostream& os, const Telegram& t) {
2 os << "message: " << t.Msg;
3 return os;
4 }
Line 1 is simply the function definition. It allows you to return the stream itself (that you pass in) so you can chain <<
segments. The operator<<
is simply the function you're providing, which is the one called when you put << tg
into an output stream statement.
Line 2 uses more basic <<
statements that have already been defined (in this case, whatever type Msg is, probably a string).
Then line 3 returns the stream, again to allow chaining of <<
segments.
The basic idea is to provide operator<<
functions that build on existing operator<<
function for the data types that constitute your type.
And with a simple wrapper class containing just an int
:
#include <iostream>
// This is my simple class.
class intWrapper {
public:
intWrapper (int x) { myInt = x; };
int getInt (void) { return myInt; }
private:
int myInt;
// Must be friend to access private members.
friend std::ostream& operator<< (std::ostream&, const intWrapper&);
};
// The actual output function.
inline std::ostream& operator<< (std::ostream& os, const intWrapper& t) {
os << "int: " << t.myInt;
return os;
}
// Main program for testing.
// Output with getter and with ostream.
int main (void) {
class intWrapper x(7);
std::cout << x.getInt() << std::endl; // ostream already knows about int.
std::cout << x << std::endl; // And also intWrapper, due to the
// function declared above.
return 0;
}
This outputs:
7
int: 7
the first by just calling the getter function to retrieve the integer, the second by calling the <<
operator function we added to ostream
.
The function you posted (henceforth "the function") is an overload of the insertion operator, operator <<
. It allows you to output an object of type Telegram
, or any other type deriving from Telegram
or convertible to a Telegram
, to an output stream. This is similar in spirit to the common use of IO streams in C++:
std::cout << 0 << '\n';
Here you output the int
0 followed by the char
newline to the standard output stream. With the function you posted you can now do something like
Telegram tel; // if Telegram has a default constructor
std::cout << tel << '\n';
something you would otherwise not been able to do, because the standard C++ library doesn't know about your new Telegram
type and so never defined how to output objects of this type.
In code:
inline std::ostream& operator<<(std::ostream& os, const Telegram& t)
The first line starts with the inline
keyword. Presumably the function is defined in a header file, and so in that case you must use the inline
keyword so the definition does not violate the one definition rule.
That is, every time you include the header in an implementation file you get the function defined for that implementation file. When the linker comes in to link together all the compiled object files it will find multiple definitions of the function, one in each implementation file that included the header with the function you posted. This is something C++ forbids; C++ demands a function may be implemented no more than one time, and exactly one time if you intend to call it.
The use of the inline
keyword essentially asks the C++ compiler to make sure that function is not defined more than once in such a way that the linker jumps off its seat and complains about the ambiguity of multiple definitions to choose from.
Here I argue it's a bad idea to inline the function. It would be a better idea to remove the inline
keyword and move the function definition to its own translation unit, or implementation file. This way the function will compile exactly once, as opposed to once for each implementation file that includes the header with the function.
Next you will notice that function is a free function, as opposed to a member function. This allows (requires, as a matter of fact) the function to specify the operator's left operand, the stream object. This means the function will work with any object that is convertible to a stream. It also means you don't need to modify a class to add this extension to output semantics. If the function would be a member then you'd have to change the header of the class, which in turns means recompiling all implementation files that include that header. Granted, it appears your function is defined in a header; that's probably a bad idea, as I explained above.
Next you see that the function returns a std::ostream
. std::ostream
is in fact typedef'd as std::basic_ostream<char, std::char_traits<char> >
, and therefore your function can only output Telegram
objects to streams that work on char
types.
The reason the function returns a std::ostream
is so that you can chain calls to the function. If you look carefully,
std::cout << 0 << 1;
is in fact a chain of two calls to the insertion operator overload function. Once to output 0, and then to output 1. It is equivalent to
std::operator<<(std::operator<<(std::cout, 0), 1);
The first call to insert 0 returns an output stream, and the second call to insert 1 takes that returned output stream and inserts into that 1. That's why we return the output stream: so we can chain calls.
You will also note that the insertion operator has a left-to-right associativity, which means that in the above statement 0 is guaranteed to be output first, and 1 second. This as opposed to the equivalent line I wrote above, in which the associativity (of function calls) is inside-out. This yields a much less readable code IMO, and that's one of the benefits of using operator <<
for output semantics.
Next look at the function's parameters. The first parameter, a std::ostream
, is taken by reference. This so you can change the stream. That's also why the stream is not taken by const
reference.
The second parameter can be taken by const
reference, because we don't want to change it, just read it. However, we do want to take it by reference, again because we don't intend to change it, not even for a local purpose, and so saving the construction of a copy can only be a good idea. It also allows for accepting derivatives of Telegram
and caling virtual functions upon them, should the need arise. Also, taking a const
reference allows you to output a temporary, as in std::cout << Telegram() << '\n';
.
{
os << "time: " << t.DispatchTime << " Sender: " << t.Sender
<< " Receiver: " << t.Receiver << " Msg: " << t.Msg;
This code should be self explanatory now. Presumably each of the members you insert into the output stream has the insertion operator defined for. The standard library defines insertion into output streams for primitives, for strings, for complex numbers and other standard types.
return os;
}
And finally you return the stream object, so the caller can chain the output with outputting another object. Note that you could simply return the result of the chain:
return os << "time: " << t.DispatchTime << " Sender: " << t.Sender
<< " Receiver: " << t.Receiver << " Msg: " << t.Msg;
Finally, to demonstrate the bonus you get from the use of a free function and taking the Telegram
parameter by reference, consider:
struct Telegram {
Telegram();
Telegram(const Communication& c);
virtual ~Telegram();
};
struct Message : public Telegram {
};
struct Letter {
operator Telegram() const;
};
// ...
Telegram t;
Communication c;
Message m;
Letter l;
std::cout << t << '\n'
<< c << '\n'
<< m << '\n'
<< l << '\n';
All using that single function for outputting a Telegram
object.
精彩评论