72

由于范围问题,我想知道这一点。例如,考虑代码

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
}

所以我的问题是,return 语句会一直复制吗?在这种情况下,它似乎有效,所以我被引导相信 return 确实复制了。如果它确实复制,它会在每种情况下都复制吗?

4

11 回答 11

51

是的,在这种情况下,将会制作一份副本。如果您像这样更改函数声明:

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

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

这与 C++ 的常见返回值优化有关,可以避免在您描述的情况下执行实际的复制操作。最终结果(或应该)与完成复制一样,但您应该注意优化。在某些情况下,这种优化的存在可以改变程序的可观察行为。

于 2009-10-07T04:18:50.327 回答
4

在你的情况下,它会返回一个副本

如果您的代码是

subline_t& subline(int, int)

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

于 2009-10-07T04:18:31.530 回答
3

是的,对于声明为返回 a 的函数structreturn此类结构将复制它(尽管编译器有权优化复制,基本上在它可以证明优化在语义上无害的情况下,您可以推理“好像”保证复制)。

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

于 2009-10-07T04:20:09.887 回答
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
}
于 2009-10-07T04:20:25.363 回答
2

它总是会返回一个副本。

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

于 2009-10-07T04:21:08.373 回答
1

在 C++ 中返回对象是按值而不是按引用完成的。

对 subline_t t 的引用超出范围

不,对象被复制。

return 语句是否总是复制

是的,不是的......从语义上讲,它的行为类似于复制,但是有一种称为返回值优化的东西可以保存复制构造函数。

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
于 2009-10-07T04:24:19.253 回答
1

它返回一个副本,这是您希望它执行的操作。将其更改为返回引用将导致分配给 line 的未定义行为。

但是,在 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.
}
于 2009-10-07T11:53:40.697 回答
1

返回的类或结构可能会被复制,也可能不会被复制,这取决于编译器是否使用复制省略。查看什么是复制省略和返回值优化的答案? 简而言之,它是否被复制取决于很多事情。

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

最后,如果您不能信任复制省略并且想要避免复制,则可以使用并返回 aunique_ptr而不是引用。对象本身不会被复制,尽管它unique_ptr本身可能会也可能不会(同样,取决于复制省略!)。但是,如果由于某种原因没有发生复制省略,则复制/移动 aunique_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();
}

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

于 2017-08-04T01:50:35.617 回答
0

对于subline_t您定义的结构,是的,它总是会返回一个副本。

于 2009-10-07T04:19:47.847 回答
-1

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

虽然这是一个语言特性,但建议不要使用它

于 2009-10-07T04:22:17.620 回答
-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 MOPs 和返回向量:5.09 MOPs 差 60%!

在调试模式下,情况要糟糕得多:

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

于 2018-11-15T10:55:37.917 回答