【问题标题】:Is it safe to hold pointers to iterators in C++?在 C++ 中保存指向迭代器的指针是否安全?
【发布时间】:2012-12-07 22:54:39
【问题描述】:

我会先问这个问题,然后是动机,最后是一个按预期编译和执行的说明性代码示例。

问题

如果我可以保证迭代器在我需要使用它的持续时间内不会失效,那么持有指向迭代器的指针是否安全(例如指向list<int>::iterator 的指针)。

动机

我有多个容器,我需要将一个容器中的项目直接交叉引用到另一个容器中的相应项目,依此类推。一个容器中的项目可能并不总是在另一个容器中具有相应的项目。

因此,我的想法是将一个指向迭代器的指针存储在容器#1 中存储的元素中,以此类推。为什么?因为一旦有了迭代器,我不仅可以访问容器 #2 中的元素,而且如果需要,我还可以擦除容器 #2 中的元素等。

如果容器#2 中有对应的元素,我会在容器#1 的元素中存储一个指向迭代器的指针。否则,此指针将设置为 NULL。现在我可以快速检查指向迭代器的指针是否为NULL,容器#2中没有对应的元素,如果不是NULL,我可以继续访问它。

那么,以这种方式存储指向迭代器的指针是否安全?

代码示例

#include <iostream>
#include <list>

using namespace std;

typedef list<int> MyContainer;
typedef MyContainer::iterator MyIterator;
typdef MyIterator * PMyIterator;

void useIter(PMyIterator pIter)
{
    if (pIter == NULL)
    {
    cout << "NULL" << endl;
    }
    else
    {
    cout << "Value: " << *(*pIter) << endl;
    }
}

int main()
{
    MyContainer myList;

    myList.push_back(1);
    myList.push_back(2);

    PMyIterator pIter = NULL;

    // Verify for NULL
    useIter(pIter);

    // Get an iterator
    MyIterator it = myList.begin();

    // Get a pointer to the iterator
    pIter = & it;

    // Use the pointer
    useIter (pIter);
}

【问题讨论】:

  • 为什么不直接保存迭代器,end迭代器的意思是NULL
  • 另外还有 Boost.Optional,这是一种通用的方式来表示“要么是一个值,要么是一个没有值的特殊情况”。因为一切都在一个地方处理,所以您不必考虑指针的引用的生命周期。
  • 保存指针(我希望)给了我两件事:1. 重量轻 2. 无需担心 end() 本身会失效。现在我承认我不确定这些担忧是否有效。
  • 感谢@SteveJessop,今天学到了一些新东西。我用optional&lt;MyIterator&gt; 尝试了 Boost Optional,它工作正常。

标签: c++ pointers iterator


【解决方案1】:

迭代器的问题在于容器上的许多操作会使它们无效(取决于所讨论的容器)。当您将迭代器保存到属于另一个类的容器时,您永远不知道何时会发生这样的操作,也没有简单的方法可以发现迭代器现在无效。

另外,直接删除属于另一个类的容器中的元素是违反封装原则的。当你想删除另一个类的数据时,最好调用该类的公共方法,然后删除数据。

【讨论】:

  • 你所说的与指针没有直接关系,如果它是真的,那么迭代器通常会毫无用处——幸运的是,情况并非如此。
【解决方案2】:

是的,这是安全的,只要您能确保迭代器不会失效并且不会超出范围。

【讨论】:

    【解决方案3】:

    迭代器通常按值处理。例如,begin()end() 将返回一个 iterator 类型的实例(对于给定的迭代器类型),而不是 iterator&amp;,因此它们每次都返回一个值的副本。 p>

    你当然可以为这个副本取一个地址,但你不能指望对begin()end()的新调用会返回一个具有相同地址的对象,并且该地址只有在你坚持的时候才有效自己到迭代器对象。

    std::vector<int> x { 1, 2, 3 };
    
    // This is fine:
    auto it = x.begin();
    auto* pi = &it;
    
    // This is not (dangling pointer):
    auto* pi2 = &x.begin();
    

    维护指向迭代器的指针几乎没有意义:迭代器已经是数据的轻量级句柄。进一步的间接性通常是设计不佳的标志。特别是在您的示例中,指针毫无意义。只需传递一个普通的迭代器。

    【讨论】:

    • 谢谢,解释得很好,理解。我无法投票,因为我没有所需的“声誉”。
    【解决方案4】:

    听起来很吓人。迭代器是一个对象,如果它离开范围,你的指针是无效的。如果您擦除容器 #2 中的对象,所有迭代器都可能变为无效(取决于容器),因此您的指针将变得无用。

    为什么不存储迭代器本身?对于容器 #1 中不引用任何内容的元素,存储 container2.end()。 只要迭代器不失效,这很好。如果是,则需要重新生成映射。

    【讨论】:

    • 我担心的一个问题是避免担心 end() 迭代器更改或失效。我不确定这是否是一个有效的问题。
    • 如果您担心这一点,您必须担心所有迭代器都会失效,然后您就无法存储它们(或指向它们的指针)。 end() 迭代器应该至少与同一容器的所有其他迭代器一样有效。
    • 你是对的,我同意。在避免分叉的同时,我一直在寻找 end() 的“恒定”标记值,它始终提供一个不变的检查目标,但是阅读此内容(例如 [stackoverflow.com/questions/6440392/…),似乎在某些情况下,被操作的项目,并且结束迭代器可能会失效。被操纵的项目很好,因为我知道我在操纵什么,但是 end() 对我来说似乎有点混乱。
    【解决方案5】:

    是的,可以像处理其他类型一样处理指向迭代器的指针,但在您的示例中,这不是必需的,因为您可以简单地将原始迭代器作为引用传递。

    一般来说,存储迭代器不是一个好主意,因为当您修改容器时,迭代器可能会变得无效。更好地存储容器并根据需要创建迭代器。

    【讨论】:

      猜你喜欢
      • 2011-10-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-13
      • 2018-09-04
      • 2021-02-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多