bob*_*obo 17 c++ stl stdvector
从函数返回stl向量时:
vector<int> getLargeArray() { ... }
Run Code Online (Sandbox Code Playgroud)
回报是否是昂贵的复制操作?我记得在哪里读过矢量分配很快 - 我应该要求调用者传递引用吗?
void getLargeArray( vector<int>& vec ) { ... }
Run Code Online (Sandbox Code Playgroud)
Ste*_*sop 23
假设你的函数构造并返回新数据,你应该按值返回,并尝试确保函数本身有一个返回类型变量的返回点vector<int>,或者最坏的几个返回点都返回相同的变量.
这确保您将在任何可靠的编译器上获得命名的返回值优化,从而消除一个潜在的副本(从函数中的值到返回值).还有其他方法可以获得返回值优化,但它并不完全可预测,因此简单的规则可以安全地进行.
接下来,您希望将返回值中的潜在副本消除为调用者对其执行的操作.这是调用者的问题,而不是被调用者,并且基本上有三种方法可以解决这个问题:
vector<int>,在这种情况下,任何可信的C++编译器都将忽略该副本.vector有移动语义.也就是说,在C++ 03中不写
vector<int> v;
// use v for some stuff
// ...
// now I want fresh data in v:
v = getLargeArray();
Run Code Online (Sandbox Code Playgroud)
代替:
getLargeArray().swap(v);
Run Code Online (Sandbox Code Playgroud)
这避免了所需的复制分配(不能省略[*])v = getLargeArray().在C++ 11中不需要它,在那里有一个便宜的移动分配而不是昂贵的复制分配,但当然它仍然有效.
另一件需要考虑的事情是你是否真的想要vector作为界面的一部分.您可以改为编写一个带有输出迭代器的函数模板,并将数据写入该输出迭代器.那么谁想要在矢量数据调用者可以传递的结果std::back_inserter,因此可以来电者谁想要在数据deque或list.事先知道数据大小的调用者甚至可以只传递一个向量迭代器(适当地为resize()d)或者一个足够大的数组的原始指针,以避免开销back_insert_iterator.有非模板方法可以做同样的事情,但它们很可能会以这种或那种方式产生呼叫开销.如果您担心复制int每个元素的成本,那么您担心每个元素的函数调用成本.
如果你的函数没有构造并返回新数据,而是它返回一些现有的当前内容vector<int>并且不允许更改原始内容,那么当你按值返回时,你无法避免至少一个副本.因此,如果该性能是一个经过验证的问题,那么您需要查看除了按值返回之外的某些API.例如,您可能提供一对可用于遍历内部数据的迭代器,一个通过索引在向量中查找值的函数,或者甚至(如果性能问题严重到需要暴露内部数据),对向量的引用.显然,在所有这些情况下,你改变了函数的含义 - 现在不是给调用者"他们自己的数据",而是提供了别人的数据的视图,这可能会改变.
[*]当然"似乎"规则仍然适用,人们可以想象一个C++实现,它足够智能,因为这是一个简单的可复制类型(int)的向量,并且因为你没有采取任何指针任何元素(我假设),然后它可以交换,结果是"好像"它复制.但我不指望它.
jua*_*nza 10
您很可能获得返回值优化,具体取决于函数体的结构.在C++ 11中,您也可以从移动语义中受益.按值返回肯定有更清晰的语义,我认为它是首选的选项,除非分析证明它是昂贵的.这是一个很好的相关文章在这里.
这是一个带有详细虚拟类的小例子,使用旧版本的GCC(4.3.4)编译时没有优化或C++ 11支持:
#include <vector>
#include <iostream>
struct Foo
{
Foo() { std::cout << "Foo()\n"; }
Foo(const Foo&) { std::cout << "Foo copy\n"; }
Foo& operator=(const Foo&) {
std::cout << "Foo assignment\n";
return *this;
}
};
std::vector<Foo> makeFoos()
{
std::vector<Foo> tmp;
tmp.push_back(Foo());
std::cout << "returning\n";
return tmp;
}
int main()
{
std::vector<Foo> foos = makeFoos();
}
Run Code Online (Sandbox Code Playgroud)
我的平台上的结果是所有复制都在函数返回之前发生.如果我使用C++ 11支持进行编译,那么push_back会导致移动副本而不是C++ 03副本构造.
| 归档时间: |
|
| 查看次数: |
16094 次 |
| 最近记录: |