Std :: ostream need help with function

I need someone to explain these lines of code to me in parts, and I need help using ostream with simple examples. thanks:).

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 does not compile and the error says:

stand :: ostream and amplifier; class :: operator <(std :: ostream & os, const Telegram & t) must take exactly one argument

+4
source share
2 answers

This line simply adds the ability to process Telegram objects into the standard output stream class.

When you add a new class and want output streams such as cout to intelligently process them, you need to add a new operator << , which has a new object type as the second argument.

What the above code does is just that. When you later follow the instructions:

 Telegram tg("Bob", "Hello, how are you?"); cout << tg; 

this function in your question will be called with the stream as the first argument, and your tg object will be called the second argument, and then it will be able to output data in a format suitable for the class.

This was actually one of the earliest versions of C ++ that I had to deal with. Although the class must be self-contained, you are actually adding something to another class to handle the output. Once you understand why this happens (because it is the ostream class, which is responsible for the output of things, and not your own class), it hopefully makes sense.


We hope this will be easier 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 just a function definition. It allows you to return the stream itself (which you are going through) so that you can chain segments << . operator<< is just the function you provide, which is called when you put << tg in the output stream operator.

Line 2 uses the more basic << operators that have already been defined (in this case, any type of Msg is probably a line).

Then line 3 returns a stream to allow a chain of << segments.

The basic idea is to provide operator<< functions that are based on the existing operator<< function for the data types that make up your type.


And with a simple wrapper class containing only 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; } 

It is output:

 7 int: 7 

the first, simply by calling the getter function to extract an integer, the second, by calling the operator function << , which we added to ostream .

+7
source

The function you sent (hereinafter referred to as the "function") is an overload of the insert operator << , operator << . It allows you to output an object of type Telegram or any other type received from Telegram or convertible to Telegram in the output stream. This is similar to the spirit of the general use of input / output streams in C ++:

 std::cout << 0 << '\n'; 

Here you output int 0 and then a new char string 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 that you otherwise could not do, because the standard C ++ library does not know about your new Telegram type and therefore has never determined how to output objects of this type.

In code:

 inline std::ostream& operator<<(std::ostream& os, const Telegram& t) 

The first line begins with the inline . Presumably, the function is defined in the header file, and therefore in this case you should use the inline so that the definition does not violate one definition rule.

That is, every time you include a header in the implementation file, you get a function defined for this implementation file. When the linker comes in to link all the compiled object files, he will find several function definitions, one in each implementation file, which includes a header with the function you published. This is what is forbidden by C ++; C ++ requires that a function can be implemented no more than once and exactly once if you intend to call it.

Using the inline keyword essentially requires the C ++ compiler to make sure that the function is not defined more than once so that the linker jumps from its place and complains about the ambiguity of several definitions to choose from.

Here, I argue that it is a bad idea to embed a function. It would be better to remove the inline and transfer the function definition to your own translation unit or implementation file. Thus, the function will be compiled exactly once, and not once for each implementation file, which includes a header with the function.

You will notice later that a function is a free function, not a member function. This allows (requires, in fact) the function to specify the operator operand on the left, the stream object. This means that the function will work with any object that is converted to a stream. It also means that you do not need to modify the class to add this extension to display semantics. If the function is a member, you will have to change the class header, which in turn means recompiling all the implementation files containing this header. Of course, it seems that your function is defined in the header; this is probably a bad idea, as I explained above.

Further you will see that the function returns std::ostream . std::ostream is actually typedef'd as std::basic_ostream<char, std::char_traits<char> > , and therefore your function can only output Telegram objects to streams that work with char types.

The reason the function returns a std::ostream is because you can associate calls with the function. If you look carefully,

 std::cout << 0 << 1; 

it is actually a chain of two calls to the overload function of the insert statement. Once for output 0, and then for output 1. This is equivalent

 std::operator<<(std::operator<<(std::cout, 0), 1); 

The first call to insert 0 returns the output stream, and the second call insert 1 accepts the returned output stream and inserts 1. Therefore, we return the output stream: therefore, we can chain the calls.

You will also notice that the insert statement has associativity from left to right, which means that in the above statement, 0 is guaranteed to be displayed first and 1 second. This is in contrast to the equivalent line that I wrote above, in which associativity (function calls) is inside out. This gives much less readable IMO code and that is one of the benefits of using operator << for output semantics.

Next, see the function parameters. The first parameter a std::ostream is taken by reference. This will allow you to change the flow. This is also why the stream is not taken by the const reference.

The second parameter can be used with the const reference, because we do not want to change it, just read it. Nevertheless, we want to take it by reference, again because we do not intend to change it, not even for local purposes, and therefore preserving the copy structure can only be a good idea. It also allows you to take derivatives from Telegram and roll virtual functions to them if the need arises. In addition, when using the const link, you can display a temporary value, as in std::cout << Telegram() << '\n'; .

 { os << "time: " << t.DispatchTime << " Sender: " << t.Sender << " Receiver: " << t.Receiver << " Msg: " << t.Msg; 

This code should now be clear. Presumably, each of the members that you insert into the output stream has an insert statement 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 a stream object, so the caller can associate the output with the output of another object. Note that you can 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 that you get from using a free function and taking the Telegram parameter from the link, 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 this single function to output the Telegram object.

+2
source

Source: https://habr.com/ru/post/1337087/


All Articles