返回STL列表作为参数

时间:2009-06-11 22:31:27

标签: c++ list stl

我有一个函数从日志文件中读取行,将这些行转换为某个类,并返回此类实例的STL列表。

我应该如何声明这个函数,以便在将它归因于调用者时不会复制整个列表?

不失一般性,假设:

list<Request> requests = log_manipulator.getAsRequestList();

我应该如何声明getAsRequestList()?我应该返回对列表的引用还是只返回一个列表?

这是一个严重的问题,因为在这个特定的赋值中,列表将包含大约1.5M的元素,因此这样的错误可能会增加内存使用量。

9 个答案:

答案 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);

output iterator

template <class OutputIterator>
void getAsRequestList(OutputIterator dest);

...

list<Request> requests;
log_manipulator.getAsRequestList( 
   insert_iterator< list<Request> >(requests, requests.begin()) );

另见:How do I return hundreds of values from a C++ function?

答案 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文章