简体   繁体   English

std :: ostream需要函数帮助

[英]std::ostream need help with function

i need someone who explain me these lines of code part by part and i need some help in using "ostream" with simple examples. 我需要有人一个一个地向我解释这些代码行,我需要帮助使用“ostream”和简单的例子。 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: 更新1:当我使用此功能时,它不会编译,错误说:

std::ostream& class::operator<<(std::ostream& os, const Telegram& t) must take exactly one argument std :: ostream&class :: operator <<(std :: ostream&os,const Telegram&t)必须只有一个参数

These line are simply adding the ability to handle Telegram objects to the standard output stream class. 这些行只是添加了将Telegram对象处理为标准输出流类的功能。

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. 当您添加一个新类并且希望输出流(如cout智能地处理它们时,您需要添加一个新的<< operator方法,该方法将新对象类型作为第二个参数。

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. 将使用stream作为第一个参数并将tg对象作为第二个参数调用您的问题中的函数,然后它将能够以适合该类的格式输出数据。

This was actually one of the early C++ things I had trouble getting my head around. 这实际上是我无法理解的早期C ++事情之一。 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. 一旦你理解了为什么会这样(因为它是负责输出事物而不是你自己的类的ostream类),它将有希望有意义。


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. 第1行只是函数定义。 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. operator<<只是你提供的函数,当你将<< tg放入输出流语句时调用它。

Line 2 uses more basic << statements that have already been defined (in this case, whatever type Msg is, probably a string). 第2行使用更基本的<<语句已经定义(在这种情况下,Msg类型,可能是一个字符串)。

Then line 3 returns the stream, again to allow chaining of << segments. 然后第3行返回流,再次允许链接<< segment。

The basic idea is to provide operator<< functions that build on existing operator<< function for the data types that constitute your type. 其基本思路是,以提供operator<<功能是建立在现有的operator<<函数构成你的类型的数据类型。


And with a simple wrapper class containing just an int : 并且使用一个只包含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 . 第一个是通过调用getter函数来检索整数,第二个是通过调用我们添加到ostream<< operator函数。

The function you posted (henceforth "the function") is an overload of the insertion operator, 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. 它允许您将Telegram类型的对象或从Telegram派生或可转换为Telegram任何其他类型的对象输出到输出流。 This is similar in spirit to the common use of IO streams in C++: 这与C ++中IO流的常见用法类似:

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

Here you output the int 0 followed by the char newline to the standard output stream. 在这里输出int 0,然后输出char newline到标准输出流。 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. 您可能无法做到的事情,因为标准C ++库不知道您的新Telegram类型,因此从未定义如何输出此类对象。

In code: 在代码中:

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

The first line starts with the inline keyword. 第一行以inline关键字开头。 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. 据推测,该函数是在头文件中定义的,因此在这种情况下,您必须使用inline关键字,以便定义不违反一个定义规则。

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 ++禁止的; C++ demands a function may be implemented no more than one time, and exactly one time if you intend to call it. C ++要求一个函数可以实现不超过一次,如果你打算调用它,恰好一次。

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. 使用inline关键字本质上要求C ++编译器确保函数不会被定义多次,以致链接器跳出其位置并抱怨可供选择的多个定义的模糊性。

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. 最好删除inline关键字并将函数定义移动到自己的转换单元或实现文件中。 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 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. std::ostream实际上是以std::basic_ostream<char, std::char_traits<char> > ,因此你的函数只能将Telegram对象输出到处理char类型的流。

The reason the function returns a std::ostream is so that you can chain calls to the function. 函数返回std::ostream的原因是您可以链接对函数的调用。 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 一旦输出0,然后输出1.它相当于

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. 第一次调用insert 0返回一个输出流,第二次调用insert 1获取返回的输出流并插入到1.这就是我们返回输出流的原因:所以我们可以链接调用。

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. 您还将注意到插入运算符具有从左到右的关联性,这意味着在上面的语句中,0保证首先输出,1秒。 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. 这产生了一个更不易读的代码IMO,这是使用operator <<作为输出语义的好处之一。

Next look at the function's parameters. 接下来看一下函数的参数。 The first parameter, a std::ostream , is taken by reference. 第一个参数是std::ostream ,它是通过引用获得的。 This so you can change the stream. 这样您就可以更改流。 That's also why the stream is not taken by const reference. 这也是const引用不采用流的原因。

The second parameter can be taken by const reference, because we don't want to change it, just read it. 第二个参数可以const引用获取,因为我们不想更改它,只需读取它。 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. 如果需要,它还允许接受Telegram衍生产品并在其上加密虚拟功能。 Also, taking a const reference allows you to output a temporary, as in std::cout << Telegram() << '\\n'; 另外,使用const引用允许您输出临时值,如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: 最后,为了演示使用免费函数获得的奖金并通过引用获取Telegram参数,请考虑:

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. 全部使用该单个函数输出Telegram对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM