返回语句复制值

时间:2009-10-07 04:15:45

标签: c++ return

由于范围问题,我对此感到疑惑。例如,考虑代码

typedef struct {
    int x1;/*top*/
    int x2;/*bottom*/
    int id;
} subline_t;



subline_t subline(int x1, int x2, int id) {
    subline_t t = { x1, x2, id };
    return t;
}

int main(){
    subline_t line = subline(0,0,0); //is line garbage or isn't it? the reference
    //to subline_t t goes out of scope, so the only way this wouldn't be garbage
    //is if return copies
}

所以我的问题是,返回语句是否总是复制?在这种情况下,它似乎工作,所以我被引导相信返回确实复制。如果它复制了,它会在每种情况下复制吗?

11 个答案:

答案 0 :(得分:40)

是的,在这种情况下会有一份副本。如果你改变了这样的函数声明:

subline_t &subline(int x1, int x2, int id) {

然后不会复制。但是,在您的特定情况下,返回对堆栈上分配的对象的引用是无效的。问题是在调用者有机会使用它之前,该对象将被破坏并失效。

这与C ++的常见Return Value Optimization有关,可以避免在您描述的情况下执行实际的复制操作。最终结果是(或应该)与复制完成时相同,但您应该了解优化。在某些情况下,此优化的存在可能会改变程序的可观察行为。

答案 1 :(得分:4)

在您的情况下,它将返回一份副本

如果您的代码是

subline_t& subline(int, int)

然后它将返回一个引用,这将产生未定义的行为。

答案 2 :(得分:3)

是的,对于声明为返回struct的函数,此类结构的return将复制它(尽管编译器有权优化副本,主要是在它可以证明优化在语义上是无害的,你可以推断“好像”复制是有保证的。)

但是,既然 将此标记为C ++而不是C,为什么不为struct提供构造函数,而不是......?看起来更清晰,更直接......! - )

答案 3 :(得分:3)

它将始终返回副本。

如果要避免在返回时复制对象的性能损失,可以声明指针,使用new构建对象的实例,然后返回指针。在这种情况下,将复制指针,但对象不会被复制。

答案 4 :(得分:2)

是的,返回是一个副本

subline_t subline(int x1, int x2, int id) {
        subline_t t = { x1, x2, id };
        return t;
}

如果你把一个引用者,那么它不是一个副本

subline_t & subline(int x1, int x2, int id) {
        subline_t t = { x1, x2, id };
        return t; // will result in corruption because returning a reference
}

答案 5 :(得分:1)

通过值而不是通过引用来完成C ++中的对象。

  

对subline_t t的引用超出了范围

不,该对象已被复制。

  

将返回语句始终复制

是但不是......在语义上它的行为与复制相似,但有一些称为返回值优化的东西可以保存复制构造函数。

foo make_foo()
{
    foo f(1,2,3);
    return f;
}

foo ff=make_foo(); /// ff created as if it was created with ff(1,2,3) -- RVO
foo ff2;
ff2=make_foo(); /// instance of foo created and then copied to ff2 and then old
                /// instance destroyed

答案 6 :(得分:1)

它返回一个副本,这是你想要它做的。更改它以返回引用将导致在赋值行中的未定义行为。

但是,在C ++中执行此操作的惯用方法是使用构造函数和赋值列表。这样可以更好地封装代码和数据结构,并允许您避免编译器可以自由构造/销毁/复制的过多中间对象。

struct subline_t {
        int x1;/*top*/
        int x2;/*bottom*/
        int id;

// constructor which initialises values with assignment list.
  subline_t(int the_x1, int the_x2, int the_id) :
    x1(the_x1),
    x2(the_x2),
    id(the_id)
  {
  }
};


int main(){
    subline_t line2(0,0,0); // never requires a copy or assignment.
}

答案 7 :(得分:0)

对于您已定义的结构subline_t,是的,它将始终返回副本。

答案 8 :(得分:0)

返回的类或结构可能会也可能不会被复制,具体取决于编译器是否使用了copy elision。查看What are copy elision and return value optimization? 的答案。简而言之,它是否被复制取决于许多事情。

您当然可以通过返回引用来避免副本。在您的示例中,返回引用无效(尽管编译器将允许它),因为本地结构已在堆栈上分配,因此返回的引用引用了已释放的对象。但是,如果对象被传递给您的函数(直接或作为对象的成员),您可以安全地返回对它的引用并避免复制返回。

最后,如果您不能信任复制省略并且您想避免复制,则可以使用并返回unique_ptr而不是引用。虽然unique_ptr本身可能是也可能不是(同样,取决于复制省略!),但不会复制对象本身。然而,如果unique_ptr的复制省略由于某种原因没有发生,则复制/移动unique_ptr非常便宜。

以下是使用unique_ptr的示例:

#include <memory>

struct A {
public:
  int x;
  int y;

  A(int x, int y) : x(x), y(y) {
  }
};

std::unique_ptr<A> returnsA() {
  return std::make_unique<A>(3, 4);
}

int main() {
  auto a = returnsA();
}

请注意,您必须(不幸地)为您的struct声明一个构造函数,否则make_unique将因C ++的不足而无法编译。

答案 9 :(得分:-1)

仅供参考,因为在这种情况下,您只使用结构(ure),它与C语言中的行为相同。

虽然这是一种语言功能,但建议不要使用

答案 10 :(得分:-2)

我不同意也不建议返回向量以更改值: 是更快的通过作为参考:

void vectorial(vector <double> a, vector <double> b, vector <double> &c)
{
    c[0] = a[1] * b[2] - b[1] * a[2]; c[1] = -a[0] * b[2] + b[0] * a[2]; c[2] = a[0] * b[1] - b[0] * a[1];
}
//This is slow!!!:
vector <double> vectorial(vector <double> a, vector <double> b)
{
    vector <double> c{ a[1] * b[2] - b[1] * a[2], -a[0] * b[2] + b[0] * a[2], a[0] * b[1] - b[0] * a[1] };
    return c;
}

我在VS2015上进行了测试,并在发布模式下获得了以下结果:

参考:8.01 MOP和返回向量:5.09 MOP更差60%!

在调试模式下,情况更糟:

参考:0.053 MOPS,返回向量:0.034 MOPs