在不使用其值的情况下调用非void函数时,是否复制了一个副本?

时间:2015-11-20 18:39:44

标签: c++ return-value

This question并没有完全帮助我澄清这个问题。说我有这个功能:

int foo(){
   /* some code */
   return 4;
}

int main(){
   foo();
}

由于我按值返回,是否会生成foo()返回的整数的副本? This answer提到编译器会将函数转换为void函数。这是否意味着被调用的实际函数将是这个?

void foo(){
   /* some code */
}

这个问题与同时使用类方法作为辅助函数和接口函数有关。例如,假设定义了类MatrixVector

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>

2 个答案:

答案 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&作为参数,期望某个对象处于某个初始可用状态,并在计算矩阵时填充它。