This question并没有完全帮助我澄清这个问题。说我有这个功能:
int foo(){
/* some code */
return 4;
}
int main(){
foo();
}
由于我按值返回,是否会生成foo()返回的整数的副本? This answer提到编译器会将函数转换为void函数。这是否意味着被调用的实际函数将是这个?
void foo(){
/* some code */
}
这个问题与同时使用类方法作为辅助函数和接口函数有关。例如,假设定义了类Matrix
和Vector
。
class Force
{
public:
Matrix calculate_matrix(Vector & input);
Vector calculate_vector(Vector & input);
private:
Matrix _matrix;
Vector _vector;
};
实施
Matrix Force::calculate_matrix(Vector & input){
/* Perform calculations using input and writing to _matrix */
return _matrix;
}
Vector Force::calculate_vector(Vector & input){
calculate_matrix(input)
/* Perform calculations using input and _matrix and writing to _vector */
return _vector;
}
每当我在Force::calculate_matrix()
中致电Force::calculate_vector()
时,我是否会复制_matrix
?每当我只需要知道Force::calculate_matrix()
的值时,我就需要_matrix
作为接口。辅助函数如:
void Force::_calculate_matrix(Vector & input){
/* Perform calculations using input and writing to _matrix */
}
然后在Force::calculate_matrix()
和Force::calculate_vector()
中使用它是一个更好的设计解决方案,或者我可以逃避上面提到的编译器优化以避免Force::calculate_matrix()
返回值的副本? / p>
答案 0 :(得分:3)
要回答这个问题,值得重新审视整个'返回'从功能的事情。一旦你开始深入挖掘它真的很有趣。
首先,int示例与后一个问题无关。请继续关注原因:)
首先要记住的是,没有“返回值”'在装配层面。相反,指导如何从函数返回值的规则在所谓的ABI协议中定义。不久前,有许多不同的ABI协议在起作用,但现在,感谢某人,我们看到的大部分内容都是所谓的AMD64 ABI。特别是,根据它,返回一个整数意味着将值推入RAX注册表。
因此,当您忽略整数返回值时,您的代码将无法读取RAX。
返回对象时会有所不同。事实上,该对象在由调用函数准备的位置返回(该位置的位置被传递给被调用者)。 Callee执行所有初始化并使用值填充对象。调用代码而不是处理'空间'同样适用。
现在,被调用函数不知道是否使用结果(除非它是内联的)。所以它总是要回归'值 - 将int放入RAX或初始化提供的空间中的对象。即使没有使用该值,在调用者站点代码仍然需要分配空间 - 因为它知道调用函数将把数据放入其中。由于调用代码知道不会使用该空间,因此它将被丢弃,并且不会在调用站点进行复制。在被叫方中仍然会有副本!
现在,更有趣。输入编译器优化。根据{{1}}函数的大小,编译器可能会决定内联它。如果发生这种情况,所有的参数都会通过'只会消失 - 没有什么可以传递的,因为代码将简单地在调用站点中执行,就好像根本没有调用任何函数一样。当发生这种情况时,就不会有任何副本,并且整个返回语句可能会被优化掉 - 无处可以返回它。
我希望,这确实可以回答这个问题。
答案 1 :(得分:0)
关于您的简单示例,我在x86_64 Linux上使用g++ -o void void.cc
编译它,并使用gdb
对其进行反汇编。这是我得到的:
(gdb) disass main
Dump of assembler code for function main:
0x00000000004004f8 <+0>: push %rbp
0x00000000004004f9 <+1>: mov %rsp,%rbp
0x00000000004004fc <+4>: callq 0x4004ed <_Z3foov>
0x0000000000400501 <+9>: mov $0x0,%eax
0x0000000000400506 <+14>: pop %rbp
0x0000000000400507 <+15>: retq
End of assembler dump.
(gdb) disass foo
Dump of assembler code for function _Z3foov:
0x00000000004004ed <+0>: push %rbp
0x00000000004004ee <+1>: mov %rsp,%rbp
0x00000000004004f1 <+4>: mov $0x4,%eax
0x00000000004004f6 <+9>: pop %rbp
0x00000000004004f7 <+10>: retq
End of assembler dump.
如您所见,foo()
通过eax
寄存器返回值4,但在main()
中忽略了该值。
关于您的Matrix代码。按值返回矩阵是昂贵的,因为必须进行复制。正如BeyelerStudios在评论中所建议的那样,您可以通过返回const Matrix &
来避免副本。另一种选择是使_calculate_matrix()
取Matrix&
作为参数,期望某个对象处于某个初始可用状态,并在计算矩阵时填充它。