返回STL容器元素的引用

时间:2012-07-31 03:54:15

标签: c++ stl reference containers

我在http://developer-resource.blogspot.com.au/2009/01/pros-and-cons-of-returing-references.html遇到的博客写道:

  

在这个代码库工作了一段时间之后我相信了   返回引用是邪恶的,应该被视为   返回一个指针,这是避免它。

     

例如,出现需要一周时间调试的问题是   以下内容:

class Foo {
        std::vector< Bar > m_vec;
      public:
        void insert(Bar& b) { m_vec.push_back(b); }
        Bar const& getById(int id) { return m_vec[id]; }
      }
  

此示例中的问题是客户端正在调用和获取   存储在向量中的引用。现在发生了什么   客户插入一堆新元素?向量需要调整大小   内部并猜测所有这些参考会发生什么?那是   对那里无效。这导致很难找到错误   通过删除&amp;。

简单地修复

我看不出代码有什么问题。我误解了参考文献和&amp; STL容器,或帖子不正确?

4 个答案:

答案 0 :(得分:4)

例如,假设你在向量中有2个元素:

ab。您可以返回这些r1r2的参考文献。

现在另一个客户端插入向量。由于向量仅存在两个元素存储。它重新分配存储。它会复制ab并在其后插入c。这会更改ab的位置。因此,引用r1r2现在无效,并指向垃圾位置。

如果getById方法没有通过引用返回,那么就会复制一切并且一切都会正常工作。

答案 1 :(得分:4)

问题更容易显示为:

std::vector<int> vec;
vec.push_back(1);
const int& ref = vec[0];
vec.push_back(ref);

vec[1]的内容未定义。在第二个push_back中,ref引用了内存vec[0]初始化时的位置。在push_back内,vector可能需要重新分配,从而使ref引用的内容无效。


这是一个很大的不便,但幸运的是,这不是经常发生的问题。 Foo容器人是否插入了他们刚刚通过ID找到的Bar?这对我来说似乎很有趣。在每次访问中发现副本似乎有点过头来解决问题。如果你觉得它够糟糕,

void insert(const Bar& b)
{
    if ((m_vec.data() <= &b) && (&b < m_vec.data() + m_vec.size()))
    {
        Bar copy(b);
        return insert(copy);
    }
    else
        m_vec.push_back(b);
}

在C ++ 11中,如此编写Foo::insert会好得多(假设Bar有一个不错的移动构造函数):

void insert(Bar b)
{
    m_vec.emplace_back(std::move(b));
}

答案 2 :(得分:2)

除了其他答案之外,值得指出的是,这种效果取决于容器类型。例如,对于vectors,我们有:

  

当成员函数必须增加时,会发生向量重新分配   包含在矢量对象中的序列超出其当前存储空间   容量。其他插入和擦除可能会改变各种存储   序列中的地址。在所有这些情况下,迭代器或   指向序列的改变部分的参考变为   无效。如果没有重新分配,只有迭代器和引用   在插入/删除点保持有效之前。

虽然lists由于存储数据的方式而在这方面更为宽松:

  

当成员函数必须插入或擦除时,会发生列表重新分配   列表的元素。在所有这些情况下,只有迭代器或引用   在受控序列的擦除部分的那一点变为   无效。

等其他容器类型。

答案 3 :(得分:0)

来自同一篇文章的评论:

  

这里的问题不是通过引用返回是邪恶的,就是这样   您返回的引用可能会变为无效。

     

第153页,第62节&#34; C ++标准库:教程和   参考&#34; - Josuttis,读到:

     

&#34;插入或删除elmeents会使引用,指针和   引用以下元素的迭代器。如果插入导致   重新分配,它使所有引用,迭代器和指针无效&#34;

     

您的代码示例与保存第一个代码示例一样邪恶   向量的元素,向量中插入1000个元素,和   然后尝试使用现有的参考。