【问题标题】:Returning a reference of STL container element返回 STL 容器元素的引用
【发布时间】:2012-07-31 03:54:15
【问题描述】:

我在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]; }
      }

此示例中的问题是客户端正在调用并获取 存储在向量中的引用。现在之后会发生什么 客户插入一堆新元素?矢量需要调整大小 内部猜测所有这些引用会发生什么?那是 就在那里无效。这导致了一个很难找到的错误 只需删除 & 即可修复。

我看不出代码有什么问题。是我误解了通过引用和 STL 容器返回,还是帖子不正确?

【问题讨论】:

    标签: c++ stl reference containers


    【解决方案1】:

    例如,您在向量中有 2 个元素:

    ab。您返回这些 r1r2 的引用。

    现在另一个客户端插入到向量中。由于向量只有两个元素存储存在。它重新分配存储。它复制ab 并在它们之后插入c。这将更改ab 的位置。所以引用 r1r2 现在无效并且指向垃圾位置。

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

    【讨论】:

      【解决方案2】:

      问题更容易显示为:

      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));
      }
      

      【讨论】:

        【解决方案3】:

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

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

        lists由于他们存储数据的方式在这方面更加放松:

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

        其他容器类型依此类推。

        【讨论】:

          【解决方案4】:

          来自同一篇文章的评论:

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

          第 153 页,第 6.2 节“C++ 标准库:教程和 参考”——Josuttis,内容如下:

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

          您的代码示例就像持有对第一个的引用一样邪恶 向量的元素,向向量中插入 1000 个元素,以及 然后尝试使用现有的参考。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2015-01-30
            • 2020-08-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-07-08
            • 2011-09-25
            • 1970-01-01
            相关资源
            最近更新 更多