我有一个函数从日志文件中读取行,将这些行转换为某个类,并返回此类实例的STL列表。
我应该如何声明这个函数,以便在将它归因于调用者时不会复制整个列表?
不失一般性,假设:
list<Request> requests = log_manipulator.getAsRequestList();
我应该如何声明getAsRequestList()
?我应该返回对列表的引用还是只返回一个列表?
这是一个严重的问题,因为在这个特定的赋值中,列表将包含大约1.5M的元素,因此这样的错误可能会增加内存使用量。
答案 0 :(得分:14)
不建议返回引用,返回列表对象会导致复制。最好的方法是将方法的签名更改为接受并填充列表引用:
list<Request> requests;
log_manipulator.getRequestListByRef(requests);
以void getRequestListByRef(list<Request>&)
作为方法的签名。
答案 1 :(得分:10)
您有两个简单的选择:
void getAsRequestList(list<Request>& requests);
...
list<Request> requests;
log_manipulator.getAsRequestList(requests);
template <class OutputIterator>
void getAsRequestList(OutputIterator dest);
...
list<Request> requests;
log_manipulator.getAsRequestList(
insert_iterator< list<Request> >(requests, requests.begin()) );
答案 2 :(得分:5)
您可以通过返回简单列表来逃避 - 搜索“返回值优化”以获取详细信息。简单地说,允许编译器生成绕过昂贵的复制构造函数的代码。
只有在尝试了这个并且对结果不满意之后,我才会推荐其他答案中建议的“填充”版本。这太丑了。
当然,需要注意的是RVO并非保证发生。
答案 3 :(得分:4)
将auto_ptr返回到列表:
auto_ptr<list<Request> > getAsRequestList()
{
auto_ptr<list<Request> > list = new list<Request>();
// populate list
return list;
}
答案 4 :(得分:2)
返回一个局部变量作为引用在C ++中很有趣,
list<Request>& requests = log_manipulator.getAsRequestList();
它依赖于编译器,因此可以在一台机器上工作,但不能在另一台机器上工作。
你最好的办法是以这种方式声明你的getAsRequestList()
void getAsRequestList(list<Request>& requests)
{
// populate results in requests
}
或在getAsRequestList()中使用新的请求并返回指针
答案 5 :(得分:1)
指向列表的指针,或者您可以将其重写为:
list<request> requests;
log_manipulator.populate_list(&requests);
答案 6 :(得分:1)
通过引用传入in / out参数绝对是我的选择。
但只是为了提供替代方案,您可以传递迭代器。通常你必须预先调整容器的大小,以便迭代器的赋值进入预先分配的槽,但STL也有一个你可以使用的后插入迭代器。
#include <iostream>
#include <iterator>
#include <list>
template<typename T>
void FillMyContainer(T inserter)
{
for(int loop=0;loop < 10;++loop)
{
(*inserter) = loop;
++inserter;
}
}
int main()
{
/*
* Example of back_inserter in use.
*/
std::list<int> data;
std::copy(std::istream_iterator<int>(std::cin),
std::istream_iterator<int>(),
std::back_inserter(data));
/*
* Using back_inserter in the context of the question
*/
std::list<int> fill;
FillMyContainer(std::back_inserter(fill));
}
所以问题现在变成为什么log_manipulator没有返回迭代器而不是列表的方法。那么您的底层实现不需要依赖于特定的容器类型吗?
答案 7 :(得分:0)
声明为
void getAsRequestList(list<Request>* requests);
并将其命名为
list<Request> requests;
log_manipulator.getAsRequestList(&requests);
答案 8 :(得分:0)
既然您已经提到了内存使用情况,您是否考虑将log_manipulator视为'iterator'或将log_manipulator视为容器并编写一个迭代它的迭代器(例如log_iter)?这样迭代器operator ++将导致从日志文件读取和解析下一行,而operator *将返回当前的“请求对象”。只要迭代器对应于STL迭代器语义,就可以在其上使用任何STL算法。
例如,您可以使用
轻松将其转换为矢量std::vector<Request> reqvector;
std::copy(log_manipulator.begin(), log_manipulator.end(), std::back_inserter(reqvector))
(假设begin(),end()函数返回'log_iter')
查看Dr. Dobb博士的Writing Your Own Iterators文章