在c++中返回std::向量的有效方法
Efficient way to return a std::vector in c++
在函数中返回std::vector
时,复制了多少数据,将std::vector放在空闲存储区(堆上(并返回指针(即:(将是一个多大的优化
std::vector *f()
{
std::vector *result = new std::vector();
/*
Insert elements into result
*/
return result;
}
比更有效率
std::vector f()
{
std::vector result;
/*
Insert elements into result
*/
return result;
}
在C++11中,这是首选方式:
std::vector<X> f();
也就是说,按值返回。
对于C++11,std::vector
具有移动语义,这意味着函数中声明的局部向量在返回时将被移动,在某些情况下,编译器甚至可以消除移动。
您应该按值返回。
该标准有一个特定的特点,即提高按价值退货的效率。它被称为"复制省略",在这种情况下更具体地说是"命名的返回值优化(NRVO("。
编译器不必实现它,但编译器也不必实现函数内联(或执行任何优化(。但是,如果编译器不进行优化,并且所有严肃的编译器都实现了内联和NRVO(以及其他优化(,那么标准库的性能可能会非常差。
当应用NRVO时,将不会复制以下代码:
std::vector<int> f() {
std::vector<int> result;
... populate the vector ...
return result;
}
std::vector<int> myvec = f();
但用户可能想这样做:
std::vector<int> myvec;
... some time later ...
myvec = f();
复制省略不会阻止此处的复制,因为它是一个赋值而不是初始化。但是,您应该仍然按值返回。在C++11中,赋值是通过一种不同的方法优化的,称为"移动语义"。在C++03中,上面的代码确实会导致复制,尽管在理论上优化器可能能够避免它,但在实践中它太难了。因此,在C++03中,您应该编写以下内容,而不是myvec = f()
:
std::vector<int> myvec;
... some time later ...
f().swap(myvec);
还有另一种选择,那就是为用户提供更灵活的界面:
template <typename OutputIterator> void f(OutputIterator it) {
... write elements to the iterator like this ...
*it++ = 0;
*it++ = 1;
}
然后,您还可以支持现有的基于矢量的接口:
std::vector<int> f() {
std::vector<int> result;
f(std::back_inserter(result));
return result;
}
如果现有代码使用reserve()
的方式比预先固定的数量更复杂,则此可能比现有代码效率更低。但是,如果您现有的代码基本上重复调用向量上的push_back
,那么这个基于模板的代码应该也一样好。
是时候发布关于RVO的答案了,我也是。。。
如果按值返回对象,编译器通常会对此进行优化,这样它就不会被构造两次,因为在函数中构造它作为临时对象然后复制它是多余的。这被称为返回值优化:创建的对象将被移动,而不是被复制。
C++11之前的一个常见习惯用法是传递对要填充的对象的引用。
那么就不会复制矢量。
void f( std::vector & result )
{
/*
Insert elements into result
*/
}
如果编译器支持命名返回值优化(http://msdn.microsoft.com/en-us/library/ms364057(v=vs.80(.aspx(,您可以直接返回矢量,前提是没有:
- 返回不同命名对象的不同路径
- 多个返回路径(即使在所有路径(,其中引入EH状态
- 返回的命名对象在内联asm块中被引用
NRVO优化了冗余的复制构造函数和析构函数调用,从而提高了整体性能。
在你的例子中不应该有真正的差异。
vector<string> getseq(char * db_file)
如果你想在main((上打印它,你应该在一个循环中完成。
int main() {
vector<string> str_vec = getseq(argv[1]);
for(vector<string>::iterator it = str_vec.begin(); it != str_vec.end(); it++) {
cout << *it << endl;
}
}
以下代码将在没有复制构造函数的情况下工作:
你的常规:
std::vector<unsigned char> foo()
{
std::vector<unsigned char> v;
v.resize(16, 0);
return std::move(v); // move the vector
}
之后,您可以使用foo例程来获取矢量,而无需复制自身:
std::vector<unsigned char>&& moved_v(foo()); // use move constructor
结果:moved_v大小为16,由[0]填充
尽管"按值返回"可能很好,但这类代码可能会导致错误。考虑以下程序:
#include <string>
#include <vector>
#include <iostream>
using namespace std;
static std::vector<std::string> strings;
std::vector<std::string> vecFunc(void) { return strings; };
int main(int argc, char * argv[]){
// set up the vector of strings to hold however
// many strings the user provides on the command line
for(int idx=1; (idx<argc); ++idx){
strings.push_back(argv[idx]);
}
// now, iterate the strings and print them using the vector function
// as accessor
for(std::vector<std::string>::interator idx=vecFunc().begin(); (idx!=vecFunc().end()); ++idx){
cout << "Addr: " << idx->c_str() << std::endl;
cout << "Val: " << *idx << std::endl;
}
return 0;
};
- Q: 执行以上操作时会发生什么?A: 堆芯转储
- Q: 为什么编译器没有发现错误?A: 因为程序语法上,虽然不是语义上,是正确的
- Q: 如果修改vecFunc((以返回引用,会发生什么情况?A: 程序运行到完成并产生预期结果
- Q: 有什么区别?A: 编译器没有必须创建和管理匿名对象。程序员已经指示编译器为迭代器和端点确定只使用一个对象,而不是像坏例子那样使用两个不同的对象
上述错误程序将表明没有错误,即使使用GNU g++报告选项-Wall-Wextra-Weffc++
如果必须生成一个值,那么以下方法可以代替调用vecFunc((两次:
std::vector<std::string> lclvec(vecFunc());
for(std::vector<std::string>::iterator idx=lclvec.begin(); (idx!=lclvec.end()); ++idx)...
上述内容在循环的迭代期间也不产生匿名对象,但是需要一个可能的复制操作(正如一些人所指出的,在某些情况下可能会被优化掉。但是引用方法保证不会产生副本。相信编译器会执行RVO并不能代替尝试构建最高效的代码。如果你能提出编译器需要执行RVO,那么你就领先了。
vector<string> func1() const
{
vector<string> parts;
return vector<string>(parts.begin(),parts.end()) ;
}
在c++11之后,这仍然是有效的,因为编译器自动使用移动而不是复制。
- 在C++中初始化向量映射的最有效方法
- 当映射包含字符串向量作为值时,从值中获取键的有效方法
- C++有效地找到向量中第一个最接近的匹配值?
- 如何有效地操作满足给定谓词的向量中的所有项目?
- 检查两个向量是否并行的最有效方法
- 当表示为对象的一维向量时,有效地旋转 NxM 矩阵 (C++)
- 如何有效地实现将向量的数据分配给多个变量?
- C++去除前x个元素的有效方法,在不改变向量大小的情况下将第x+1个元素推到第一个
- 如何在C++中有效地将数组移动到向量
- 将一种数据类型的向量复制到同一数据类型的结构向量中的有效方法是什么
- 如何有效地用第二个值对向量对进行分组
- 从长(且合理)稀疏向量中选择随机元素的最有效方法是什么?
- 如何在 CUDA 中(有效地)将大量向量相互比较
- 基于整数向量执行位排列的有效方法?
- 如何使用C++有效地合并排序与向量
- C++:检查向量中的元素是否大于另一个具有相同索引的元素的有效方法?
- 如何有效地规范化向量C++
- remove_if然后擦除对向量有效吗
- 将一个STL向量有效地分配给另一个STL矢量(WSL问题)
- c++向量有效地添加元素