临时对象问题

Johnny_de 2006-04-12 07:02:24
比如我重载了+运算符,然后返回integer对象
return integer (left.i + right.i) ;
书上说:"这样情况时,编译器明白对创建的对象没有其他需求,只是返回它,所以编译器直接地把这个对象创建在返回值外面的内存单元。因为不是真正创建一个局部对象,所以仅需要单个的普通构造函数调用(不需要拷贝构造函数),并且不会调用析构函数。因此,这种方法不需要什么花费,效率是非常高的。

我的问题是:1.里面说指的外面的内存单元指的是什么?
2.既然调用了普通的构造函数,为什么退出作用域时不会调用析构函数?
...全文
270 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
Johnny_de 2006-04-14
  • 打赏
  • 举报
回复
to QTPIG() ( ) 信誉:100
我觉得你的理解不对。
sankt 2006-04-14
  • 打赏
  • 举报
回复
to sankt(黄景天) ( ) 信誉:110
2.既然调用了普通的构造函数,为什么退出作用域时不会调用析构函数?
//==================
因为类里面没有动态内存分配,所以退出作用域时不会调用析构函数

你这种判断是不对的,不是只有对态分配内存才会调用析构函数。

///================
这里我说错了
不好意思啊
当一个对象离开了它的作用于后,它的生命周期结束
就会调用析构函数
Johnny_de 2006-04-13
  • 打赏
  • 举报
回复
我现在理解为什么return integer (left.i + right.i) ;情况下不调用析构函数,是因为没有调用拷贝构造函数的原因。这时唯一一个对象只是创建在返回值外面的内存里。

而像这种情况:有个类H,类里有个方法H F(H); 类H里我定义了拷贝构造函数。
这时,我运用临时对象, F(h),h为已经生成的一个H对象,这时,会调用类的拷贝构造函数和析构函数,因为这时这个临时对象是不需要的,所以系统会调用析构函数尽快析构掉这个临时对象。
Johnny_de 2006-04-13
  • 打赏
  • 举报
回复
to sankt(黄景天) ( ) 信誉:110
2.既然调用了普通的构造函数,为什么退出作用域时不会调用析构函数?
//==================
因为类里面没有动态内存分配,所以退出作用域时不会调用析构函数

你这种判断是不对的,不是只有对态分配内存才会调用析构函数。
howyougen 2006-04-13
  • 打赏
  • 举报
回复
mark
QTPIG 2006-04-13
  • 打赏
  • 举报
回复
另外个人认为这个时候不是临时对象
QTPIG 2006-04-13
  • 打赏
  • 举报
回复
我觉得
1.里面说指的外面的内存单元指的是什么?
楼主没有把话理解全 人家本来说得是"返回值外面的内存单元" 外面是修饰返回值的 不是修饰内存单元 开始我还确实有点混了 现在很清楚了 本来正常的定义是在此被调用函数内部定义要返回的值 然后复制给调用函数 现在因为"编译器明白对创建的对象没有其他需求,只是返回它,所以编译器直接地把这个对象创建在返回值外面的内存单元。"

2.既然调用了普通的构造函数,为什么退出作用域时不会调用析构函数?
根据我对1的解释 再结合局部对象的说明 就不难理解 这里的定义是直接定义在调用函数 也就是说它的作用域是调用函数 不是被调用函数 因此在被调用函数要结束时无需析构 只有在调用函数要结束时才会析构

个人意见 如果有意见可以讨论 ^_^
Johnny_de 2006-04-13
  • 打赏
  • 举报
回复
没人发表意见了吗?今晚结贴
cunsh 2006-04-12
  • 打赏
  • 举报
回复

但是书上说很多编译器都做named return value(NRV)优化.
所以只要在所有返回的地方return的是同一个对象.编译器最后也不对返回值调用拷贝构造函数.
cunsh 2006-04-12
  • 打赏
  • 举报
回复

<<inside the c++ object model>> 的 2.3 程序转化语意学 的 "返回值的初始化"


X bar()
{
X xx;
// ...
return xx;
}
转换后的c++伪代码变成了:
伪代码如下:
void bar( X& __result ) //加上了额外参数 __result
{
X xx;
// ...
__result.X::X( xx ); //调用拷贝构造函数.拷贝要返回的值到目的地址
return;
}
所以你直接return integer (left.i + right.i) ;
就会转换为伪代码:

__result.integer::integer(left.i + right.i) //调用integer的构造函数.但不是拷贝构造函数
return;


jixingzhong 2006-04-12
  • 打赏
  • 举报
回复
对临时对象的处理还是一样的,
析构函数作用的是 tmp 对象 ......
jixingzhong 2006-04-12
  • 打赏
  • 举报
回复
是不是3处调用析构函数是因为拷贝构造函数里面new了新的对象的原因?
================================
不是
doinglu 2006-04-12
  • 打赏
  • 举报
回复
首先要知道临时类如何使用

比如:
class MyInt
{
public:
MyInt() { printf("Constructing...\n"); }
~MyInt() { printf("Destructing...\n"); }
};

然后使用以下测试代码:
MyInt GetMyInt()
{
return MyInt();
}

int main(int argn, char *argv[])
{
GetMyInt();
printf("Done of GetMyInt().\n");
return -1;
}
运行后你应看到以下结果:
Constructing... // 在函数GetMyInt()中
Destructing... // 在函数main()中
printf("Done of GetMyInt().\n"); // 在函数main()中,GetMyInt()后

也就是class MyInt的实例的确存在过,并且曾经构造并析构。
实际上,在GetMyInt()返回时,return会在堆栈中找到返回内存,并以此调用构造函数;在返回以后,main使用完毕了此类(本例中并没有任何使用)后,调用了类的析构函数,然后继续。

所谓的外面的内存,实际上就是指上一级函数栈的空间,也就是承载返回值的空间,一般来说,返回值都是使用寄存器(如x86上一般都使用ax/eax/rax),但是如果返回的是结构、类,则是内存。

+------------------+-----------------------+--------------+
| GetMyInt() stack | GetMyInt() parameters | main() stack |
+------------------+-----------------------+---------------
在return时,前面的stack & parameters都会被弹出(add esp, xxx),然后再保留一个空间作为返回值(sub esp, sizeof(class MyInt)),然后以esp为指针构造类。返回以后,main可以直接使用并在使用完毕以后析构。

为了进一步分析,采用以下例子:

class MyInt
{
public:
MyInt() { printf("Constructing...\n"); }
~MyInt() { printf("Destructing...\n"); }
operator +(class MyInt &m2) { printf("Add!\n"); };
};

MyInt GetMyInt()
{
return MyInt();
}

int main(int argn, char *argv[])
{
GetMyInt() + GetMyInt();
printf("Done of GetMyInt().\n");
return -1;
}

运行以后应该看到:
Constructing... // 第一次调用GetMyInt()函数内
Constructing... // 第二次调用GetMyInt()函数内
Add! // main()函数内
Destructing... // main()函数内,表达式完毕
Destructing... // main()函数内,表达式完毕
Done of GetMyInt().
看到此例的结果,应该明白构造、析构的情况。

关于栈的情况,可以将类的构造函数修改为:
MyInt() { int _esp; __asm { mov _esp, esp }; printf("Constructing... %p, esp = %p\n", this, _esp); }

运行后,结果类似如下(具体值会有不同,和机器、编译器、操作系统相关)
Constructing... 0012FF6C, esp = 0012FE58
Constructing... 0012FF70, esp = 0012FE54
Add!
Destructing...
Destructing...
Done of GetMyInt().
可以注意到,两次construct的this地址偏移差是sizeof(MyInt),和esp相当接近(不会相等,因为构造函数本身也需要使用栈)
Johnny_de 2006-04-12
  • 打赏
  • 举报
回复
但是如果这样integer tmp(left.i + right.i); return tmp;
这样会发生三件事:1.tmp对象被创建与此同时它的构造函数被调用.
2.拷贝构造函数把tmp拷贝到返回值外部存储单元里
3.当tmp在作用域结尾时调用析构函数.
是不是3处调用析构函数是因为拷贝构造函数里面new了新的对象的原因?具体拷贝构造函数默认实现我不太清楚了,得看看.
Johnny_de 2006-04-12
  • 打赏
  • 举报
回复
哦对.第2个问题我问的似乎太傻了一点,呵呵,脑子短路了。
sankt 2006-04-12
  • 打赏
  • 举报
回复
我的问题是:1.里面说指的外面的内存单元指的是什么?
//====================
这里我认为是栈

2.既然调用了普通的构造函数,为什么退出作用域时不会调用析构函数?
//==================
因为类里面没有动态内存分配,所以退出作用域时不会调用析构函数

64,701

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧