如何在方法主体中返回声明向量的引用?
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;
}
当然,这没有优化,我想返回这个向量的引用,但我不能简单地这样做:
const vector<float>& MyObject::getResults(int n = 1000)
{
vector<float> results(n, 0);
// do some stuff
return results;
}
这不起作用,向量将在方法结束时被销毁,因为它是一个局部变量。
所以我找到解决这个问题的唯一解决方案是在 MyObject 中创建一个私有向量并返回对该向量的引用:
const vector<float>& MyObject::getResults(int n = 1000)
{
this->results.clear();
this->results.resize(n, 0);
// do some stuff
return results;
}
这是正确的方法吗?您还有其他解决方案可以提出吗?
什么最有效?
按值返回。别担心,不会发生复制。这是最佳做法:
// 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());
这是如何工作的?
当函数返回对象时,它以以下两种方式之一返回它:
- 在寄存器中
- 在内存中
您只能在寄存器中返回简单对象,如int
s 和double
s。对于内存中返回的值,函数将向该函数传递一个指针,指向放置返回值所需的位置。
当您调用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
这没有优化
没事的。具体来说,从 C++11 开始,您无需在此处执行任何额外的操作。
无论如何,只有在您拥有正确的内容以及对其进行分析的方法之后,您才应该担心优化。
无论如何,返回对私有向量的引用并不理想 - 它不必要地延长了向量的生存期,并且可能会像任何其他有状态函数一样导致以后的重入问题。
- 声明高维向量的更简洁的方法
- 标准::向量声明中使用的模板参数
- 使用全局声明的向量时,C++双重释放错误/损坏
- 为什么这种向量声明无效?
- 无法在声明时使用初始值设定项列表初始化常量字符*/字符串数组的向量
- 如何声明一个标准::提升直方图的向量?提升直方图的类型是什么?
- 在类中声明向量的大小
- C++ 通过函数声明后初始化向量
- 向量索引变量声明(size_t 或 std::vector<DATATYPE>::size_type)
- 如何声明指向类对象的指针向量?
- 我不明白这个向量声明语句
- <string> C++ 中的向量声明
- 使用更大值和向量声明优先级队列
- 将向量声明为类成员
- 全局和主类中的向量声明
- C++如何将对象的向量声明为类的成员
- 如何将unique_ptr的向量声明为类数据成员?
- 对齐数据类型Eigen::矩阵的数组或向量声明
- 看不懂这个向量声明
- 将对象向量声明为另一个类的成员