cout 堆栈,operator<< 运算符重载输出问题

在C++中cout的输出流当中,有一些问题很容易出错,就比如下面这道简单程序,看似简单,但却是一个值得深思的问题~~

#include <iostream>

using namespace std;

int foo(int &x)

{

cout << "第" << x << "次调用" << " ";

return ++x;

}

int main()

{

int i = 1;

cout << foo(i) << ‘\t‘ << foo(i) << endl;

}

很多人一开始会以为输出的结果会是:

第1次调用 2 第2次调用 3

但是结果却是:

第1次调用 第2次调用 3 2

为什么嘞??? 首先需要了解的是:

进程空间:

代码区----------------存放程序的执行代码

全局数据区------------存放全局数据,常量,文字量,静态全局量和静态局部量

堆区------------------存放动态内存,供程序随机申请使用

栈区------------------函数数据区(局部数据区)

(以上是运行中的内存布局)

函数调用的整个过程就是栈空间操作的过程。函数调用时,C++作以下工作:

(1)建立被调函数的栈空间,其大小由函数定义体中的数据量决定;

(2)保护调用函数的运行状态和返回地址;

(3)传递参数;

(4)将控制权转交给被调函数;

(5)函数运行完成后,复制返回植到函数局部数据块底部;

(6)恢复被调函数的运行状态;

(7)返回调用函数。

调用一个函数可以看作是一个栈中元素的压栈与退栈操作。

有了以上的介绍,解释过程就能知道了:

原始输出语句: cout << foo(i) << ‘\t‘ << foo(i) << endl;

cout输出的执行顺序是从右向左执行的,so:

1:执行<<endl,因为没有std::cout对象,无法执行,压栈

2:执行<<foo(i),执行foo(1),输出:第1次调用,得到返回值2,还是因为没有std::cout对象,无法马上输出,压栈

此时,代码变成cout << foo(i) << ‘\t‘ << 2 << endl;

3:执行<< ‘\t‘ ,没有std::cout对象,无法马上输出,压栈

此时,代码变成cout << foo(i) << ‘\t‘ << 2 << endl;

4:执行<<foo(i),执行foo(2),输出:第2次调用 得到返回值3,还是因为没有std::cout对象,无法马上输出,压栈

此时,代码变成cout << 3 << ‘\t‘ << 2 << endl;

对于以上过程还需注意两点:

1. 【int& x】 也就是说 x实际上是i的别名

2. cout << foo(i) << ‘\t‘ << foo(i) << endl;

<==> operator << (cout, foo(i)的返回值, ‘\t‘, foo(i)的返回值, endl); 参数从右到左压入堆栈区:

时间: 2024-08-01 17:00:45

cout 堆栈,operator<< 运算符重载输出问题的相关文章

为什么operator&lt;&lt;&gt;&gt;运算符重载一定要为友元函数呢?

如果是重载双目操作符(即为类的成员函数),就只要设置一个参数作为右侧运算量,而左侧运算量就是对象本身...... 而 >>  或<< 左侧运算量是 cin或cout 而不是对象本身,所以不满足后面一点........就只能申明为友元函数了... 如果一定要声明为成员函数,只能成为如下的形式: ostream & operator<<(ostream &output) { return output; } 所以在运用这个<<运算符时就变为这种形

Swift语言精要 - Operator(运算符重载)

运算符重载 Swift的这一语言特性或许应该启发于C++ class Vector2D { var x : Float = 0.0 var y : Float = 0.0 init (x : Float, y: Float) { self.x = x self.y = y } func +(left : Vector2D, right: Vector2D) -> Vector2D { let result = Vector2D(x: left.x + right.x, y: left.y + r

[转]C++之运算符重载(1)

在前一节中曾提到过,C++中运行时的多态性主要是通过虚函数来实现的,而编译时的多态性是由函数重载和运算符重载来实现的.这一系列我将主要讲解C++中有关运算符重载方面的内容.在每一个系列讲解之前,都会有它的一些基础知识需要我们去理解.而运算符重载的基础就是运算符重载函数.所以今天主要讲的是运算符重载函数. 1.运算符重载是对已有的运算符赋予多重含义,使同一个运算符作用域不同类型的数据导致不同行为的发生.比如 1 int i;2 int i1=10,i2=10;3 i=i1+i2;4 std::co

C++之运算符重载(1)

在前一节中曾提到过,C++中运行时的多态性主要是通过虚函数来实现的,而编译时的多态性是由函数重载和运算符重载来实现的.这一系列我将主要讲解C++中有关运算符重载方面的内容.在每一个系列讲解之前,都会有它的一些基础知识需要我们去理解.而运算符重载的基础就是运算符重载函数.所以今天主要讲的是运算符重载函数. 1.运算符重载是对已有的运算符赋予多重含义,使同一个运算符作用域不同类型的数据导致不同行为的发生.比如 int i; int i1=10,i2=10; i=i1+i2; std::cout<<

C++基本之 运算符重载

=====>友元运算符#include <iostream> using namespace std; class Test { public: Test(int a = 0) { Test::a = a; } friend Test operator +(Test&,Test&); friend Test& operator ++(Test&,int); public: int a; }; Test operator +(Test& temp1,

c++运算符重载1

在前一节中曾提到过,C++中运行时的多态性主要是通过虚函数来实现的,而编译时的多态性是由函数重载和运算符重载来实现的.这一系列我将主要讲解C++中有关运算符重载方面的内容.在每一个系列讲解之前,都会有它的一些基础知识需要我们去理解.而运算符重载的基础就是运算符重载函数.所以今天主要讲的是运算符重载函数. 1.运算符重载是对已有的运算符赋予多重含义,使同一个运算符作用域不同类型的数据导致不同行为的发生.比如 1 int i; 2 int i1=10,i2=10; 3 i=i1+i2; 4 std:

C++运算符重载——输入/输出运算符

为了与IO标准库一致,重载输入输出运算符函数的第一个行参应该是流的引用,第二个行参是对象的引用. 如果重载为类的成员函数,第一个行参应该是对象的引用,第二个行参是流的引用. 使用方式是 ClassObj << cout 这样与标准IO库就不一致了,所以输入输出运算符不能重载为类的成员函数,可以重载为类的友元函数和普通函数. 通常重载输出运算符的第二个行参是const的,因为输出一个类不许要更改它: 但是重载输入运算符的第二个行参必须是非const的,否则无法赋值. 重载的基本方法如下: //重

C++ Primer笔记10_运算符重载_赋值运算符_进入/输出操作符

1.颂值运营商 首先来福值运算符引入后面要说的运算符重载.上一节说了构造函数.拷贝构造函数:一个类要想进行更好的控制.须要定义自己的构造函数.拷贝构造函数.析构函数.当然,还有赋值运算符.常说的三大函数就是指拷贝.赋值.析构. 假设一个类不定义自己的赋值运算符.会自己生成一个默认的赋值运算操作.这个默认的赋值运算满足一般类的需求.它实现的是一个浅拷贝.可是当类的功能.作用逐渐完好时,就会出现非常多问题.所以,通过自己定义赋值运算符来控制赋值操作时类的行为是非常有必要的.当一个类的对象与对象之间发

C++ 输入、输出运算符重载

C++ 能够使用流提取运算符 >> 和流插入运算符 << 来输入和输出内置的数据类型.我们可以重载流提取运算符和流插入运算符来操作对象等用户自定义的数据类型. 在这里,有一点很重要,我们需要把运算符重载函数声明为类的友元函数,这样我们就能不用创建对象而直接调用函数. 下面的实例演示了如何重载提取运算符 >> 和插入运算符 <<. #include <iostream> using namespace std; class Person{ publ