怎么做“<<<和“>>”运营商做I / O?

时间:2012-10-17 16:25:33

标签: c++

  

可能重复:
  Operator overloading

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

如果你看一下这行代码

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

我意识到这是什么。它将“firstvalue is x”写入控制台。 x是firstvalue的值。但是,我对“&lt;&lt;”不知道任何事情。或“&gt;&gt;”双角括号。我无法研究它们或它们做什么,因为我不知道它们的正式名称。

我的问题是,上述声明中实际发生了什么(一步一步)?什么是这些“&lt;&lt;”对于?我想我明白cout是用于写入控制台的标准库函数。但是我习惯于使用objective-c或点符号。我没有看到这个“cout”函数是什么对象。

我可以更轻松地理解printf,因为至少它为参数提供了大括号。例如printf(“你的字符串在这里”)。

5 个答案:

答案 0 :(得分:16)

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

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

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

循序渐进

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

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

编译器基本上将object << object表达式转换为函数调用。

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

然后它会找出要调用的过载。 (这真的是 棘手。现在,仅仅相信它就足够了 查找具有匹配参数的operator<<重载。)

basic_ostream的大多数内置重载都是herehere

答案 1 :(得分:4)

<<运算符是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()

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

答案 2 :(得分:2)

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

所以,这个:

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等)不是函数:它们是全局对象。这里的函数是重载的<<运算符。将它们视为FILE *stdout, *stderr等价物。

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

  • type-safety:您不能错误地使用"%f"打印整数并获取垃圾,因为重载决策会在编译时自动选择std::ostream& operator<<(std::ostream&, double)函数
  • 支持用户定义的类型:你可以为你的奇怪的新类编写一个流插入操作符,它随处可用
  • 流抽象:你可以使用相同的重载(所以你只写一次)来格式化为stdout和stderr(cout / cerr)和文件(std::ofstream)和字符串(std::ostringstream)。无需单独处理printf / fprintf / snprintf

还有一些缺点:

  • 性能:所有抽象都有一些损失,以及在运行时配置的语言环境系统的一般性
  • 详细程度:至少对于printf已经支持的原始类型,格式字符串更加简洁,更具表现力

答案 3 :(得分:2)

<<是运算符,+是运算符,*是运算符。以下是等效表达式的方式:

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

下两个也是如此:

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

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

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

您可以通过提供带有一个参数的成员函数operator<<operator>>来重载特定类的移位运算符(操作符右侧的操作数)。或者,您可以提供具有两个参数的相同名称的非成员函数,分别是运算符的两个操作数。

答案 4 :(得分:1)

以下是语法糖:

// 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);