【问题标题】:How do the "<<" and ">>" operators do I/O? [duplicate]“<<”和“>>”运算符如何进行I/O? [复制]
【发布时间】:2012-10-07 23:33:13
【问题描述】:

可能重复:
Operator overloading

我正在期待已久的 C++ 回归,有一些基本的符号在其他语言中似乎并不那么突出。

如果你看这行代码

cout << "firstvalue is " << firstvalue << endl;

我意识到 这是做什么的。它将“firstvalue is x”写入控制台。 x 是 firstvalue 的值。但是,我对“>”双尖括号一无所知。由于我不知道它们的正式名称,因此我无法研究它们或它们的作用。

我的问题是,在上述陈述中 究竟发生了什么(逐步)?这些“

我可以更容易理解 printf ,因为至少它为参数提供了大括号。例如printf("你的字符串在这里").

【问题讨论】:

标签: c++


【解决方案1】:

C++ 允许运算符重载。这意味着用户定义的类型可以在内置运算符上定义自己的行为。在这种情况下,运算符称为:left shiftright shift 运算符。这些运算符传统上用于位移,但标准库将它们重新用于符号化流操作。

您可以在C and C++ here 中找到可用运算符的列表。

在您的情况下,您将字符串文字和某种类型的值流式传输到std::cout,这是std::basic_ostream 类型的对象。

一步一步

应用优先规则后,您的代码如下所示:

((cout << "foobar") << x) << endl;

编译器基本上会将object &lt;&lt; object 表达式转换为函数调用。

operator<<(operator<<(operator<<(cout, "foobar"), x), endl);

然后它会找出调用哪个重载。 (这真的是 棘手。就目前而言,相信它只是 查找具有匹配参数的 operator&lt;&lt; 的重载)。

basic_ostream 的大多数内置重载是herehere

【讨论】:

  • 接受字符串文字的重载实际上并不是一个成员,它是我链接的免费函数之一。
【解决方案2】:

&lt;&lt; 运算符是 C++ 中的“算术左移”。例如:

3 << 2

计算为12。原因是3的二进制表示是

00000011

向左移动两次得到

00001100

结果的数值是12。

它与输出有什么关系?什么都没有,其实。但是在 C++ 中,由于重载,您可以重新定义运算符的含义。 C++ 标准库决定将左移运算符的含义重新定义为一种“发送到流”。

那么会发生什么

std::cout << "whatever"

返回值std::cout,但作为副作用,它输出字符串“whatever”。

之所以选择该运算符,是因为它具有合理的优先级(重载无法更改优先级,并且您无法定义新的运算符),并且形状使其看起来有些“自然”。但是请注意,左移运算符只是一个普通运算符,例如不能保证评估顺序:

std::cout << f() << g() << h();

这里的输出将是调用f()的结果,然后是调用g()的结果,然后是调用h()的结果...但是函数本身可能以不同的顺序调用例如h() 可以先调用!

因此从某种意义上说,运算符的“序列外观”具有误导性,因为它是关于输出序列,而不是关于评估序列。

【讨论】:

    【解决方案3】:

    &lt;&lt; 是一个运算符,同样+ 是一个运算符,* 是一个运算符。顺便说一下,以下是等价的表达式:

    5 + 3 + 2
    ((5 + 3) + 2)
    

    接下来的两个也是:

    std::cout << "Hello" << std::endl
    ((std::cout << "Hello") << std::endl)
    

    它只是一个有两个操作数的运算符。对于基本类型,&lt;&lt;&gt;&gt; 运算符实际上称为左移和右移运算符。它们执行按位移位。例如,5 &lt;&lt; 1 会将 5 (0101) 中的所有位左移一位,得到 10 (1010)。

    但是,与大多数其他运算符一样,您可以重载移位运算符。在输入/输出库的情况下,移位运算符被重载以提供输入和输出到流的自然语法。这是因为 &lt;&lt;&gt;&gt; 标记的方向性看起来像是某种东西以一种或另一种方式流动。使用这些 I/O 类,这些运算符重载会返回对您正在对其执行运算符的流的引用,以便它们可以链接在一起。

    您可以通过提供一个成员函数operator&lt;&lt;operator&gt;&gt; 来为特定类重载移位运算符,该函数接受一个参数(运算符右侧的操作数)。或者,您可以提供具有相同名称的非成员函数,它们接受两个参数,分别是运算符的两个操作数。

    【讨论】:

      【解决方案4】:

      它们被称为流插入(或提取,在istream &gt;&gt; 的情况下),实际上是左移和右移运算符的语义重载。

      所以,这个:

      int x = 1 << 1;
      

      有点偏移,但是这个:

      std::cout << x;
      

      是流插入。您可以将其明确写为:

      operator <<(std::cout, x);
      

      并得到完全相同的结果。流插入运算符的传统格式(它们可以为用户定义的类型重载,因此编写自己的类型并不罕见)是

      std::ostream& operator <<(std::ostream&, T value);
      

      返回输出流(通过引用),因此您可以链接调用:您的示例翻译为:

      operator<< (
        operator<< (
          operator<<(std::cout, "firstvalue"),
          firstvalue
        ),
        std::endl
      );
      

      哦,还有...std::cout(和std::cerr 等)不是函数:它们是全局对象。这里的函数是重载的&lt;&lt; 运算符。将它们视为 FILE *stdout, *stderr 等价物。

      printf 等相比,C++ iostream 有一些优势。人:

      • 类型安全:您不会错误地使用"%f" 打印整数并得到垃圾,因为重载解析会在编译时自动选择std::ostream&amp; operator&lt;&lt;(std::ostream&amp;, double) 函数
      • 对用户定义类型的支持:您可以为自己的新类编写一个流插入运算符,它可以在任何地方工作
      • 流抽象:你可以使用相同的重载(所以你只写一次)格式化到标准输出和标准错误(cout/cerr),以及文件(std::ofstream)和字符串(std::ostringstream) .无需单独处理printf/fprintf/snprintf

      也有一些缺点:

      • 性能:所有这些抽象以及在运行时配置的语言环境系统的通用性都会受到影响
      • 冗长:至少对于 printf 已经支持的原始类型,格式字符串更简洁,更具表现力

      【讨论】:

        【解决方案5】:

        它是以下的语法糖:

        // Let the function 'print' be a renaming of 'operator<<'
        // with T being the type of the object you want to print.
        std::ostream& print(std::ostream&, const T&);
        
        // 1) Print "first value is" and then return the stream you
        // to which to just printed (ie. cout). 2) Use the returned
        // stream to chain function calls and print 'firstValue'.
        print(print(std::cout, "first value is"), firstValue);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2018-05-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-04-21
          • 1970-01-01
          • 2014-08-16
          相关资源
          最近更新 更多