上面说到,在返回对象时,“在函数中自动产生了一个(对我们而言)不可见的临时对象,它保存了要返回的值”。下面以一个简单的例子,从反汇编代码来观察这个不可见的临时对象。
C++ 代码如下:
[C++] 纯文本查看 复制代码 #include <iostream>
using namespace std;
class myclass {
int a;
int b;
public:
void setab(int i, int j) { a = i; b = j; }
};
myclass input()
{
myclass temp;
temp.setab(11, 14);
return temp;
}
int main()
{
myclass ob;
ob = input();
return 0;
}
使用 g++ 的 -S 选项生成反汇编代码。由于及时这么一小点程序,反汇编代码也很多,下面只拿出关键部分来说明。
main() 函数部分:
[Plain Text] 纯文本查看 复制代码 .LCFI8:
movl %esp, %ebp
.LCFI9:
pushl %ecx
.LCFI10:
subl $36, %esp #开辟局部变量空间
.LCFI11:
leal -32(%ebp), %eax
movl %eax, (%esp)
call _Z5inputv #调用 input() 函数
subl $4, %esp
movl -32(%ebp), %eax #-32(%ebp) 和 -28(%ebp) 就是副本中a 和 b 两个变量的地址
movl -28(%ebp), %edx
movl %eax, -16(%ebp) #对 main() 里的对象 ob 中的成员 a 和 b 赋值
movl %edx, -12(%ebp)
movl $0, %eax
movl -4(%ebp), %ecx
leave
leal -4(%ecx), %esp
ret
input() 函数代码:
[Plain Text] 纯文本查看 复制代码 _Z5inputv:
.LFB1405:
pushl %ebp
.LCFI2:
movl %esp, %ebp
.LCFI3:
pushl %ebx
.LCFI4:
subl $36, %esp #开辟 input() 函数的栈帧
.LCFI5:
movl 8(%ebp), %ebx #%ebx 里存放的就是“临时对象”的首地址
movl $14, 8(%esp) #setab() 参数入栈(两个整数 11 和 14 )
movl $11, 4(%esp)
movl %ebx, (%esp)
call _ZN7myclass5setabEii #该函数就是 setab()
movl %ebx, %eax # temp 对象的副本地址通过 %eax 来返回,但此时副本已经创建完成,故而在返回到main()中时不会用到这个%eax
addl $36, %esp
popl %ebx
popl %ebp
ret $4
_ZN7myclass5setabEii 对应的是 setab() 函数,代码如下:
[Plain Text] 纯文本查看 复制代码 _ZN7myclass5setabEii:
.LFB1404:
pushl %ebp
.LCFI0:
movl %esp, %ebp
.LCFI1:
movl 8(%ebp), %edx # 8(%ebp) 中存放 temp 副本中 a 的地址,该地址位于 main() 栈帧
movl 12(%ebp), %eax #从 input() 函数的栈帧里取得参数,这里是11
movl %eax, (%edx)
movl 8(%ebp), %edx
movl 16(%ebp), %eax #从 input() 函数的栈帧里取得参数,这里是14
movl %eax, 4(%edx) #4(%edx) 中存放 temp 副本中 b 的地址,该地址位于 main() 栈帧
popl %ebp
ret
在 input() 函数里,由于最后会返回 temp 这个对象,所以在调用 setab() 进行设置 temp 时,temp 对象中的 a 和 b 的值并没有直接保存在 input() 函数的栈帧里,而是放在 main() 的栈帧里,也就是说,temp 对象的 a 和 b 和 副本的 a 和 b 共用了 main() 栈帧中的同一块内存区域里的值。这样,当 input() 函数返回时,在销毁自身时,它的栈帧也会被弹出,但是由于副本是存放在 main() 里的,因此不会被销毁,这样当回到 main() 里时,才能实现 ob = input() 这样的赋值操作。 |