如何 return 方法体中声明的向量的引用?

How to return the reference of a declared vector in the method body?

我有这个方法:

vector<float> MyObject::getResults(int n = 1000)
{
    vector<float> results(n, 0);
    // do some stuff
    return results;
}

当然这没有优化,我想 return 这个向量的参考,但我不能简单地这样做 :

const vector<float>& MyObject::getResults(int n = 1000)
{
    vector<float> results(n, 0);
    // do some stuff
    return results;
}

这不起作用,向量将在方法结束时销毁,因为它是局部变量。

所以我发现解决这个问题的唯一方法是在 MyObject 中创建一个私有向量并 return 对此向量的引用:

const vector<float>& MyObject::getResults(int n = 1000)
{
    this->results.clear();
    this->results.resize(n, 0);
    // do some stuff
    return results;
}

这样做正确吗?您还有其他解决方案吗?

什么最有效?

Return 按值。别担心,不会发生复制。 这是最佳做法:

// Use this
vector<float> getResults(int n = 1000);

这是为什么? 从函数 编辑的局部变量 return 未被复制 。它们被移动到将存储 return 值的位置:

// Result moved into v; no copying occurs
vector<float> v = getResults(); 

// Result moved into memory allocated by new; no copying occurs
vector<float>* q = new vector<float>(getResults()); 

这是如何工作的?

当一个函数 return 是一个对象时,它 return 以两种方式之一来实现它:

  • 在寄存器中
  • 记忆中

您只能在寄存器中 return 简单对象,例如 intdouble。对于内存中的 returned 值,函数将传递一个指向它需要放置 return 值的位置的指针。

当您调用 new vector<float>(getResults()); 时,会发生以下情况:

  • 计算机为新向量分配内存
  • 它将内存的位置连同任何其他参数一起提供给 getResults()
  • getResults在那个内存中构造向量,不需要复制。

如何return引用成员变量?

一般来说,这是一个过早的优化可能不会提供太多或任何好处,并且它使您的代码更复杂,更容易出现错误

如果您将 getResults 的输出分配给向量,那么数据无论如何都会被复制:

MyObject m; 
vector<float> = m.getResults(); // if getResults returns a const reference, the data gets copied

另一方面,如果将 getResults 的输出分配给 const reference,这会使 MyObject 的生命周期管理变得更加复杂。在下面的示例中,您 return 的引用在函数结束后立即失效 因为 m 被销毁了。

vector<float> const& evilDoNotUseThisFunction() {
    MyObject m;
    vector<float> const& ref = m.getResults();
    return ref; // This is a bug - ref is invalid when m gets destroyed
}

std::vector复制和移动有什么区别?

复制向量的所有元素。复制向量时,向量存储的所有数据都会被复制:

vector<float> a = getVector(); // Get some vector

vector<float> b = a // Copies a

这相当于下面的代码:

vector<float> a = getVector(); // Get some vector

vector<float> b(a.size()); // Allocate vector of size a

// Copy data; this is O(n)
float* data = b.data();
for(float f : a) {
    *data = f;
    data++;
}

移动不会遍历任何元素。当向量由move构造时,就好像它与空向量交换一样:

vector<float> a = getVector(); // Get some vector

vector<float> b = std::move(a); // Move a into b

相当于:

vector<float> a = getVector(); // Get some vector

vector<float> b; // Make empty vector (no memory allocated)

std::swap(a, b); // Swap a with b; very fast; this is O(1)

TL;DR:复制会循环复制所有数据。移动只是换出谁拥有内存。

我们怎么知道 results 被移动了? C++11 要求局部变量 自动 当它们被移动时已 returned。您不必致电 move.

交换真的发生了吗?在很多情况下,没有。交换已经很便宜了,但是 编译器可以聪明地完全优化掉交换。 它通过在内存中构建 results 向量来做到这一点 returning results。这称为命名 Return 值优化。参见 https://shaharmike.com/cpp/rvo/#named-return-value-optimization-nrvo

Of course this is not optimized

没关系。具体来说,自 C++11 起,您无需在此处执行任何额外操作。

在任何情况下,只有当您拥有正确的东西并找到分析它的方法后,您才应该担心优化。

无论如何,返回对私有 vector 的引用并不理想 - 它不必要地延长了 vector 的生命周期,并且可能会在以后导致重入问题,就像任何其他有状态函数一样。