How do the “<<” and “>>” operators do I/O?
Possible Duplicate:
Operator overloading
我期待着回到C++,有一些基本的符号似乎并不突出,在其他语言。
如果你看这行代码
1 | cout <<"firstvalue is" << firstvalue << endl; |
我知道这是怎么回事。它将"firstvalue is x"写入控制台。x是FirstValue的值。但是,我对"<"或">>"双尖括号一无所知。我没能研究它们,也没能研究它们的作用,因为我不知道它们的正式名称。
我的问题是,在上面的陈述中到底发生了什么(一步一步的)?这些"<<"是干什么用的?我想我理解cout是一个标准的库函数,用于编写控制台。不过,我习惯了使用Objective-C或Dot符号。我不知道这个"cout"函数是什么对象的成员。
我可以更容易地理解printf,因为它至少为参数提供了大括号。例如printf("此处为您的字符串")。
C++允许运算符重载。这意味着用户定义的类型可以在内置运算符上定义自己的行为。在这种情况下,这些操作符被称为:
您可以在这里找到C和C++中可用操作符的列表。
在您的例子中,您正在将一个字符串文字和某个类型的值流式传输到std::cout中,后者是std::basic_Ostream类型的对象。
一步一步
应用优先规则后,代码如下所示:
1 | ((cout <<"foobar") << x) << endl; |
编译器将基本上把
1 | operator<<(operator<<(operator<<(cout,"foobar"), x), endl); |
然后它会找出调用哪个重载。(这是真的狡猾的就目前而言,只要相信查找带有匹配参数的
基本类的大多数内置重载在这里和这里。
EDCOX1×0运算符是C++中的"算术左移位"。例如:
1 | 3 << 2 |
计算结果为12。原因是3的二进制表示是
1 | 00000011 |
向左移动两次
1 | 00001100 |
结果的数值为12。
它与输出有什么关系?实际上,什么都没有。然而,在C++中,由于过载,可以重新定义运算符的含义。C++标准库决定将左移位操作符的含义重新定义为"发送到流"的类型。
所以发生的是
1 | std::cout <<"whatever" |
返回值
之所以选择该运算符,是因为它具有合理的优先级(重载不能更改优先级,并且不能定义新的运算符),并且该形状使它看起来有点"自然"。但是请注意,左移位运算符只是一个普通运算符,例如,无法保证评估顺序:
1 | std::cout << f() << g() << h(); |
这里的输出是调用
所以在某种意义上,操作符的"序列外观"是误导的,因为它是关于输出序列的,而不是关于评估序列的。
1 2 | 5 + 3 + 2 ((5 + 3) + 2) |
接下来的两个是:
1 2 | std::cout <<"Hello" << std::endl ((std::cout <<"Hello") << std::endl) |
它只是一个有两个操作数的运算符。对于基本类型,
但是,与大多数其他运算符一样,您可以重载移位运算符。在输入/输出库的情况下,移位运算符被重载,以便为流的输入和输出提供自然的语法。这是因为
可以通过提供一个成员函数
它们被称为流插入(或提取,在
所以,这个:
1 | int x = 1 << 1; |
有点偏移,但这:
1 | std::cout << x; |
是流插入。您可以将其明确地写为:
1 | operator <<(std::cout, x); |
得到完全相同的结果。流插入操作符的传统格式(对于用户定义的类型,可以重载它们,因此编写自己的类型并不少见)是
1 | std::ostream& operator <<(std::ostream&, T value); |
输出流被返回(通过引用),因此您可以链接调用:您的示例翻译为:
1 2 3 4 5 6 7 | operator<< ( operator<< ( operator<<(std::cout,"firstvalue"), firstvalue ), std::endl ); |
哦,还有……
C++ IoFielts在EDCOX1上有一些优点,例如9。
- 类型安全:您不能错误地打印带有
"%f" 的整数并得到垃圾,因为重载分辨率在编译时自动选择std::ostream& operator<<(std::ostream&, double) 函数。 - 支持用户定义的类型:您可以为您的Whizzy新类编写一个流插入操作符,它在任何地方都可以工作。
- 流抽象:可以使用相同的重载(因此只写一次)来格式化为stdout和stderr(
cout /cerr ),以及文件(std::ofstream 和字符串(std::ostringstream )。不需要单独处理printf /fprintf /snprintf 。
还有一些缺点:
- 性能:对于所有的抽象,以及在运行时配置的本地系统的通用性,都会受到一些惩罚。
- 冗长:至少对于已经被
printf 支持的原语类型,格式字符串更简洁、更具表现力。
它是用于以下内容的句法糖:
1 2 3 4 5 6 7 8 | // 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); |