C++ nontrival class 值传递的机制。

来源:互联网 发布:知乎周刊是什么 编辑:程序博客网 时间:2024/06/06 01:42

研究C++已经有一段时间了,我那时扔掉了手头的中文版c++ primer,开始阅读英文版,用时一年半多才勉强开完一遍,增加了一千一百多个注释,如果确认没有版权问题,我会把它共享。再给gcc提bug时,通过邮件交流得知C++标准化工作组的网站,http://open-std.org/JTC1/SC22/WG21/,后面有陆续接触了C++创始人写的书,以及他在自己home page http://www.stroustrup.com/发表的文章。也浏览Nicolai M. Josuttis关于标准库和template的书,并为其找到几处错误,还一直期待他的C++11 template相关的书籍。目前在研究GCC中C++标准库的实现,也在看lippman的 inside the c++ object model。


今天看到class作为函数参数传递的问题,解决了困扰自己很久的问题,回想起来,其实好多类似的问题,但我没有总结出来,尽管我水平不高,这些问题别人也可能碰到,总结出来总会帮助到别人的,后面我会慢慢回忆那些让我豁然开朗的情形,写出来。接下来进入正题。


在C++中,object有一种叫做memberwise的copy方式,函数参数传递就是用的这样的机制。对于类似int,char,或者c中的structure,这都不是问题,因为对于它们,memberwise copy实现的拷贝构造函数是合法的。大家都清楚,memberwise copy方式实现的拷贝构造并不总是合法的。那么如果memberwise copy不合法时,如何实现函数参数传递呢? 编译会采用下面的策略:

1、直接把作为参数的object构造在被调用的函数栈里

2、在calling function stack中构造函数参数,然后将called function parameter改写成引用形式。


GCC默认采用了第二种方式,下面是我用GDB验证的结果。


  1 #include <iostream>  2   3 class test  4 {  5     private:  6         int i;  7     public:  8         test(int v):i(v){}  9         test(const test &v):i(v.i + 1){} 10         virtual void show() 11         { 12             std::cout << i << std::endl; 13             return; 14         } 15         ~test() = default; 16 }; 17  18 int foo(test v) 19 { 20     int ii = 0; 21     v.show(); 22     std::cout << ii << std::endl; 23     return 0; 24 } 25  26 int main() 27 { 28     test v(1); 29     foo(v); 30     return 0; 31 }


可执行程序gdb调式的结果


Breakpoint 1, foo (v=...) at how_class_argument_passed.cpp:20
20  int ii = 0;
(gdb) info r
eax            0xbffff588 -1073744504
ecx            0xbffff5b0 -1073744464
edx            0x2 2
ebx            0x48996000 1218011136
esp            0xbffff550 0xbffff550
ebp            0xbffff568 0xbffff568

esi            0x0 0
edi            0x0 0
eip            0x80486f2 0x80486f2 <foo(test)+6>
eflags         0x286 [ PF SF IF ]
cs             0x73 115
ss             0x7b 123
ds             0x7b 123
es             0x7b 123
fs             0x0 0
gs             0x33 51
(gdb) print &v
$1 = (test *) 0xbffff588
(gdb) f 1
#1  0x08048773 in main () at how_class_argument_passed.cpp:29
29  foo(v);
(gdb) info r
eax            0xbffff588 -1073744504
ecx            0xbffff5b0 -1073744464
edx            0x2 2
ebx            0x48996000 1218011136
esp            0xbffff570 0xbffff570
ebp            0xbffff598 0xbffff598

esi            0x0 0
edi            0x0 0
eip            0x8048773 0x8048773 <main()+65>
eflags         0x286 [ PF SF IF ]
cs             0x73 115
ss             0x7b 123
ds             0x7b 123
es             0x7b 123
fs             0x0 0
gs             0x33 51
(gdb) print &v
$2 = (test *) 0xbffff580


可以看到函数foo的stack是0xbffff550 - 0xbffff568,但是值传递的参数地址是0xbffff588,明显不在foo stack中,在main stack中。

main stack 0xbffff570 - 0xbffff598,以及调用foo的argument的地址0xbffff580。

这说明调用foo时,参数是复制了,但是复制的object依然在主调函数的栈中。也就是上面说的第二种情况。





0 0
原创粉丝点击