什么是最有效的?
按值返回。不用担心,不会发生复制。这是最佳做法:
// Use this
vector<float> getResults(int n = 1000);
为什么会这样?函数返回的局部变量不会被复制。它们被移动到将存储返回值的位置:
// 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());
这是如何工作的?
当一个函数返回一个对象时,它以两种方式之一返回它:
您只能在寄存器中返回简单的对象,例如 ints 和 doubles。对于内存中返回的值,函数会传递一个指向它需要放置返回值的位置的指针。
当您拨打 new vector<float>(getResults()); 时,会发生以下情况:
- 计算机为新向量分配内存
- 它将内存的位置与任何其他参数一起提供给
getResults()。
-
getResults 在该内存中构造向量,无需复制。
返回一个成员变量的引用怎么样?
一般来说,这是一种过早的优化,可能不会提供太多或任何好处,并且它会使您的代码更复杂,更容易错误。
如果您将getResults 的输出分配给一个向量,那么无论如何都会复制数据:
MyObject m;
vector<float> = m.getResults(); // if getResults returns a const reference, the data gets copied
另一方面,如果将getResults 的输出分配给const reference,这会使管理MyObject 的生命周期变得更加复杂。在下面的示例中,您返回的引用在函数结束后立即失效,因为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 要求局部变量在返回时自动移动。您不必致电move。
是否真的发生了交换?在许多情况下,不会。交换已经很便宜了,但是编译器可以很聪明并完全优化交换。它通过在内存中构造你的results 向量来实现这一点,它将返回results。这称为命名返回值优化。见https://shaharmike.com/cpp/rvo/#named-return-value-optimization-nrvo