前言
今天我同学问我这样一个问题:
为什么二三行cout输出的不一样
于是我结合了一些文章视频与自己的理解,写了这篇文章。
一、cout与<<究竟是什么
<iostream>文件中的std::cout配合左移运算符完成打印
这是cout在<iostream>文件中的定义,是一个ostream对象
_EXPORT_STD extern "C++" __PURE_APPDOMAIN_GLOBAL _CRTDATA2_IMPORT istream cin;
_EXPORT_STD extern "C++" __PURE_APPDOMAIN_GLOBAL _CRTDATA2_IMPORT ostream cout;
实际上cout是这么一回事:
ostream& operator<<(ostream& cout, Type& a)
// 我们以cout<<(Type)的形式调用上面的函数
Effective C++一书中说,要以当年C++开发者写内置类型时的态度写自定义类,那么两者必有共同之处。假如我们自定义了一个类Int,里面只有一个int成员变量,我们就需要重载左移运算符:
class Int {
friend void operator<<(ostream& cout, Int& a);
friend ostream& operator<<(ostream& cout, Int& a);
public:
Int(int i)
: i_(i) {}
int i_;
};
// cout 属于 ostream 类型
void operator<<(ostream& cout, Int& a)
{
cout << a.i_ << endl;
} // 不好,因为这样写,cout << a;合法,但 cout << a << a;不合法
// 为了实现上面没能实现的无限追加输入的链式编程思想,可以像下面这样写
ostream& operator<<(ostream& cout, Int& a)
{
cout << a.i_ << endl;
return cout;
} // 解决了上面的问题
然后把它作为int类的友元,这样才有以下的运算:
Int i = 1; // 单参构造函数,隐式构造
cout << i << i << endl;
到现在,无限追加输入的链式编程,究竟是怎么执行这件事,我们可以略知一二了:
ostream& operator<<(ostream& cout, Type& a)
{
// TODO
/*在这里执行打印操作*/
return cout;
}
// 以下是“伪代码”
// 事情是这样的:
// 1- << 传入两个参数 cout 和 (type) -- (type)泛指某种类型的对象
cout << (type) << (type) << (type);
// << 调用结束返回一个 &cout ,cout引用自动解析,就变成了下面的样子
// 2- 返回的cout 与第二个(type) 再次作为 << 的参数
cout << (type) << (type);
// 再次返回一个 &cout
cout << (type);
// 这次的返回值&cout没人接收了,于是自动释放返回值。
二、++放左边跟放右边有什么区别
一般来说,前置自增(也就是++放左边)调用速度比后置自增快,原因是前者采用引用传递pass-by-reference而后者采用值传递pass-by-value。
但是在左移运算符链中,两者就有明显的区别,就像我一开始放的那张图一样。
int func1()
{
cout << "print 1" << endl;
return 1;
}
int func2()
{
cout << "print 2" << endl;
return 2;
}
cout << func1() << func2() << endl;
/*
输出结果:
print 2
print 1
12
*/为什么
原来,在cout链中,函数从右向左调用,返回值从左往右打印。
结语
假如读者看懂了一、二两大点,那么文章一开始的问题就可以迎刃而解了,快去试一试吧!
我承认上面说的基本上没用,但是对C++内置类型的原理探讨还是很有趣的,何况它还是面试题呢
reference:
[1] cout顺序,i++和++i,*p++和*++p_cout *p-CSDN博客
[2] 41 类和对象-C++运算符重载-递增运算符重载_哔哩哔哩_bilibili