关于析构、函数调用堆栈、返回临时变量造成的疑惑

在复习OOP的时候发现一个有关函数调用和析构的怪异问题,下面是代码:

#include <iostream>
using namespace std;

class ctest
{
public:
	ctest(int x=0) : a(x) { cout << "构造对象(" << a << ")\n"; }
	~ctest(){ cout << "析构对象(" << a << ")\n";}
	ctest & Add()								// 本成员函数为引用返回
	{
		++a;
		return *this;
	}
	ctest add()									// 本成员函数为值返回
	{
		ctest temp(*this);
		++a;
		return temp;
	}
	friend ostream & operator<<(ostream &out, const ctest &c)
	{
		out << c.a;
		return out;
	}
private:
	int a;
};
int main()
{
	ctest a(100), b(200);
	cout << a << ", " << b << endl;	// 第3行输出
	a.Add().Add();
	b.add().add().add().add().add();							// 拷贝构造临时无名对象时无输出
	cout << a << ", " << b << endl;	// 第6行输出
	a.~ctest();								// 主动调用析构函数,并不销毁对象
	b.~ctest();								// 主动调用析构函数,并不销毁对象
	cout << a << ", " << b << endl;	// 对象a,b仍可被访问。第9行输出
	cout << "返回操作系统。" << endl;	// 第10行输出
	return 0;
}

问题出在哪里呢?

就是在主函数第四行b对add()反复调用的过程中发现的问题,具体什么情况呢?

按照我们常规思考,应该是正常析构,比如200,201,202,203,204……

可以看到,析构对象第一次200后续都是201,为什么呢?

嗯……不知道程序各项函数怎么调用的时候,GDB是个好东西!我们打断点调试一下吧!

好的,我们开始调试了,可以看到a是102,b现在还是200,正常

进入其中

ok我们可以看到temp在还没拷贝的时候随机被分配一个数据成员的值,this就是我们外面的b,a的值是200,正常

记住this的地址0x5ffe34

ok, 下一步,++a之前temp已经拷贝构造完成了,a变成了200

在debug console中输入&temp,得到地址0x5ffe4c

++a,*this的a变成了201,正常

return temp

欸?!问题来了,终端没有析构信息出现,this的地址变成了0x5ffe4c,并且值是200,正好是之前的temp。

其实这也在我们预料中,返回的是temp,先前没对temp做修改,改的是*this,这个时候改的*this其实才是先前 return 的 temp。

再继续,直接展示本次调用函数结束

进入下一次调用,地址变成0x5ffe48

ok,此时情况大致明了了,我们直接跳出这个语句

跳出之后,析构函数以先进先出的队列形式调用输出析构对象的情况。

大致想通了,首先,我们的b调用add(),注意b在这段代码中是不会被析构的!还有我们的析构调用一定是按照栈的先进后出形式调用的!

其实这样一看就很明了了

首先b拷贝构造一个temp(200),随后b的a+1 = 201,返回temp,之后不管b了,此时temp调用add()

temp作为this,拷贝构造一个新的temp(200),旧的temp的a+1 = 201,随后返回新的temp,新的temp,旧的temp放在函数调用临时堆栈的底部,值为201,在所有函数调用结束前,都不会再管它。

随后我们接着反复这个过程。

最后一步略有不同,因为不会再利用返回的temp调用新的add(),因此直接return放在栈顶。

函数调用结束,从栈顶到栈底依次调用析构函数

输出情况正好就是200,201,201,201,201!

完美解决

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值