从函数返回stl容器的最佳方法(性能方面)是什么?返回的容器通常包含数千个项目。
方法1:
typedef std::list<Item> ItemContainer;
ItemContainer CreateManyItems() {
ItemContainer result;
// fill the 'result' ...
return result;
}
ItemContainer a = CreateManyItems();
方法2:
void CreateManyItems(ItemContainer &output) {
ItemContainer result;
// fill the 'result' ...
output.swap(result);
}
ItemContainer a;
CreateManyItems(a);
方法3:
void std::auto_ptr<ItemContainer> CreateManyItems() {
std::auto_ptr<ItemContainer> result(new ItemContainer);
// fill the 'result' ...
return result;
}
std::auto_ptr<ItemContainer> a = CreateManyItems();
还是有更好的方法吗?
答案 0 :(得分:8)
如果您只想填充std::list
项,则可以使用std::fill
或std::fill_n
或标准算法功能组合。
由于不清楚您希望填写列表的具体内容是什么/如何,因此无法准确评论您的代码。除非您执行使用标准算法函数无法完成的非常特殊或复杂的操作,否则请使用标准算法函数。如果它们没有帮助你,那么只有第一种方法,编译器可能会优化代码中的返回值,从而消除不必要的副本,因为大多数编译器都实现了RVO。
在维基百科上查看以下主题:
在stackoverflow本身看到这些有趣的主题:
Dave Abrahams的文章:
我仍然会强调这一点:您是否看到了<algorithm>
标题提供的all the generic functions?如果没有,那么我建议你首先查看它们,看看它们中的任何一个(或它们的组合)是否可以在你的代码中做你想做的事情。
如果您想创建并填写列表,则可以使用std::generate()
或std::generate_n
功能。
答案 1 :(得分:6)
我通常使用方法4(几乎与方法2相同):
void fill(ItemContainer& result) {
// fill the 'result'
}
ItemContainer a;
fill(a);
答案 2 :(得分:4)
我会使用方法1并希望编译器优化掉返回值的副本。
答案 3 :(得分:2)
方法1可以从复制省略中受益,但以下非常相似的代码不能:
ItemContainer a = CreateManyItems();
// do some stuff with a
a = CreateManyItems();
// do some different stuff with a
这需要C ++ 0x移动语义,以便有效地避免复制容器。当你设计你的功能时,你不知道客户将如何使用它,因此以这种方式依赖复制省略可能会让人感到不愉快。他们在C ++ 03中的修复方法如下,他们应该警惕他们需要它的情况:
ItemContainer a = CreateManyItems();
// do some stuff with a
CreateManyItems().swap(a);
由于这与方法2基本相同,因此您可以同时提供1 和 2作为重载,这会向调用者提示他们应该考虑潜在的性能陷阱。
如果集合中的Items没有相互引用,我首选的API形式如下,但它取决于您所暴露的接口类型 - 因为它是一个模板,实现必须在编译调用者(除非您可以提前预测它将使用的类型以及extern
这些特化):
template <typename OutputIterator>
void CreateManyItems(OutputIterator out) {
*out++ = foo; // for each item "foo"
}
ItemContainer a;
CreateManyItems(std::back_inserter(a));
// do some stuff with a
a.clear();
CreateManyItems(std::back_inserter(a));
现在,如果调用者更愿意将项目放在双端队列中,因为他们想要随机访问,那么他们只需要调整他们的代码:
std::deque<Item> a;
CreateManyItems(std::back_inserter(a));
或者如果他们根本不需要它们,他们可以编写一个流式算法,在其输出迭代器中完成它的工作,并节省大量内存。
你可以改为编写一个迭代器类,一个接一个地生成Items,但趋于更加繁琐,因为控制流是“由内而外”,并不一定会使调用者代码看起来更好(见证istream_iterator
)。
答案 4 :(得分:1)
方法1没问题。它被称为复制椭圆,你会发现它自动应用于将方法1转换为方法2,基本上,但它的维护方式不那么难看。
链表?如果您甚至模糊地注重表现,请使用std::vector
。
答案 5 :(得分:1)
其中,排名第三的可能性能最佳。也许稍微更好,更灵活,是没有swap
的数字2的变体 - 只是直接填充容器。当然,这不会是例外安全的。
答案 6 :(得分:0)
使用pimpl习语将容器封装在适当的类中。然后按值传递/返回该类。
答案 7 :(得分:0)
有一种更好的方式,我很惊讶没有一个答案指出这一点。你基本上使用输出迭代器。这使您的功能独立于容器并且更加灵活。
template<typename OutIter>
void CreateManyItems(OutIter it)
{
//generate some data
*it++ = 1;
*it++ = 2;
*it++ = 3;
}
以下是您如何使用它:
void main()
{
//use array as output container
int arr[3];
CreateManyItems(arr);
//use vector as output container
std::vector<float> v;
CreateManyItems(std::back_inserter(v));
}