【问题标题】:C++ Set Iterator in ClassC++ 在类中设置迭代器
【发布时间】:2011-01-18 21:12:29
【问题描述】:

我有一个班级,其中有一个名为 _Emails 的成员。它的类型为set<PEmail*>。当我在成员函数中并运行以下代码时,它按预期工作:

for( set<PEmail*>::iterator it=this->_Emails->begin(); it!=this->_Emails->end(); ++it )
  { 
    cout << "Email: " << (*it)->getEmail() << endl;
  }

也就是说,它会生成一个电子邮件地址列表。现在,我希望能够随着班级的增长而添加到这个集合中,并且不会失去我上次迭代的位置。我试图让我的班级的另一个成员,名为_EmailItr,类型为set&lt;PEmail*&gt;::iterator。我在构造函数中初始化它,就在我的 _Emails 之后,如下所示:

this->_Emails = new set<PEmail*>;
this->_EmailItr = this->_Emails->begin();

然后,尝试执行类似的 for 循环:

// send the max queue amount, or until there are no more emails                                         
for( int i=0;  i<this->_QueueSize || i==_Emails->size(); ++i )
  {
    cout << "i: " << i << endl;
    cout << "QueueSize: " << this->_QueueSize << endl;
    cout << "Emails: " << this->_Emails->size() << endl;

    cout << (*_EmailItr)->getEmail() << endl;
  }

我得到以下输出:

i: 0
QueueSize: 2
Emails: 2
Segmentation fault

什么给了?我是否试图以不正确的方式使用迭代器?

【问题讨论】:

  • 以下划线和大写字母开头的标识符保留用于所有目的的实现。我建议在您遇到非常奇怪的错误之前更改这些标识符并摆脱下划线大写的习惯。
  • 附带说明,如果您想使用有序容器(即std::setset:map 及其多个对应物)来存储指向对象的指针(set&lt;PEmail*&gt;)而不是您的对象的副本' 否则需要提供严格的弱排序谓词,否则您会遇到一些意外......
  • @David:他们被保留是什么意思?我从来没有被告知过,你能告诉我吗?谢谢!
  • 这是标准的第 17.4.3.1.2 节:某些名称和函数签名集始终保留给实现: — 每个名称包含双下划线 _ 或以下划线开头,后跟大写字母 (2.12) 保留给实现以供任何使用。 — 每个以下划线开头的名称都保留给实现,用作全局命名空间中的名称。

标签: c++ iterator segmentation-fault


【解决方案1】:

当您在创建std::map 后立即获得this-&gt;_Emails-&gt;begin() 时,您获得的迭代器就是结束迭代器(因为std::map 中没有元素,它的大小为零且begin() == end())。您不能取消引用或增加结束迭代器。它并不指向“容器的开始”;它指向“当前位于开头的元素”,或者如果容器为空,则返回结束迭代器。

(在不相关的注释中,您似乎做了太多的动态分配。为什么要动态创建std::set 对象(使用new)?为什么不只是将std::set 作为成员上课?)

【讨论】:

    【解决方案2】:

    当您首先创建集合_Emails,然后将_EmailItr 设置为_Emails-&gt;begin() 时,迭代器没有指向任何东西。其实就是指向_Emails-&gt;end(),因为当集合为空时,begin()end()是一样的。

    所以稍后,您尝试取消引用指向 end() 的迭代器,这是未定义的行为并导致您的崩溃。

    当您将新对象添加到列表中时,您似乎假设迭代器实际上以某种方式在幕后“更新”。它不是。它总是指向end()。您需要将其设置为_Emails-&gt;begin()您将元素插入集合之后。


    此外,与您的问题无关,但是:您是否有某些原因需要在堆上分配 _Emails

    【讨论】:

      【解决方案3】:

      在迭代期间插入新元素时,STL set 表现良好。特别是,如果您在添加新元素的同时迭代 set,您将不会“忘记”您所在的位置;您的迭代器将保持有效,它将看到添加或删除的任何新元素。我不完全确定你的用例是什么,但我认为你不需要你在这里介绍的所有附加机器。只需使用没有任何修饰的老式迭代器就可以了。

      【讨论】:

      • 我拥有的是一组正在排队的电子邮件地址。当 100 /more/ 添加到其中时,或者应用程序完成排队时,我需要它转储 100 或剩余的 SMTP 队列。所以,我需要一个全局迭代器,这样我就可以在对该函数的后续调用之间跟踪最后排队的电子邮件。而且,我不能只在每个 for 循环之后转储集合,因为我需要维护它的状态以防止重复。
      • @Josh- 出于好奇,您为什么不在这里使用std::queue 而不是set?或者可能是std::deque?这似乎比std::set 更适合您的工作。
      • @Josh:std::set 按排序顺序存储其元素,而不是按插入顺序。您是否按排序顺序将电子邮件插入到集合中?如果没有,我认为您描述的场景不会起作用。
      • @templatetypedef:双端队列或队列不需要弹出元素才能访问吗?正如我所说,我需要在对象的整个生命周期中维护对象的元素。我使用 set 是因为有人建议它比向量快,我想如果我使用迭代器,我可以不用访问索引就可以逃脱。
      • @James:哦,废话,我忘了!
      猜你喜欢
      • 2020-11-08
      • 1970-01-01
      • 2015-09-30
      • 1970-01-01
      • 2012-03-16
      • 2014-03-21
      • 2015-08-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多