【问题标题】:STL linked list:: how to access members?STL链表::如何访问成员?
【发布时间】:2012-10-13 22:05:01
【问题描述】:

如果我有一个包含指向类的指针的 STL 列表,并且想要访问类成员,我该怎么做?具体来说,我需要能够删除列表中的成员,每个成员都有一个具有唯一 ID 的成员。

所以我有类似的东西:

class Actor{

    private:
    int id;

    public:
    int getActorID(){ return id;};
};

std::list<Actor *> actorList;

std::list<Actor *>::iterator i;

所以如果每个演员都有一个唯一的 id,我怎样才能删除具有特定 ID 的演员?我一直在使用手动编码的链接列表,但我想将其切换到 STL。唯一的问题是我不知道如何访问方法 getActorID() 来查找要删除的节点。感谢您的帮助。

【问题讨论】:

  • 您确定列表是您想要的吗?这听起来更像是在搜索地图。
  • 请重新考虑使用链表。如果您要经常遍历列表,请使用向量。如果您需要与对象关联的唯一 id,请使用映射(或 c++11 中的 unordered_map)。链表对于大多数用途来说都很糟糕。 futurechips.org/thoughts-for-researchers/…

标签: c++ list stl iterator


【解决方案1】:
std::list<Actor *>::iterator it;

std::list<Actor *>::iterator iStart = actorList.begin() ;
std::list<Actor *>::iterator iEnd = actorList.end() ;
for (it=iStart ;it!=iEnd;++it)
{
if (*it->getActorId() == searchedId)
  {
   actorList.erase(it);
   break; //you have unique id's so you could delete a maximum 1 item
  }
}

别忘了你还有其他选择,比如

std::list::remove
std::list::remove_if

http://en.cppreference.com/w/cpp/container/list/remove

【讨论】:

  • 成功了。感谢您的帮助(适用于所有回答的人!)
  • 这有效吗? delete 在该表达式中不起作用,还有其他问题。
  • 你是对的——我只是用擦除替换了删除行。但是,我的意思是它在我能够访问节点成员的意义上起作用。但是,该实施不起作用。正如许多海报所指出的那样,这份名单并不理想。我也尝试过矢量,但也有问题。类 Actor 是一个基类,其顶部有一个派生类,所有这些都在创建时从堆中分配。是的,我必须非常频繁地遍历节点,所以我对如何最好地实现它持开放态度。他们是游戏的游戏演员。
  • @user1609525 这是对您的问题的一个相当好的答案——仅关于删除列表元素。您可能希望接受答案并提出新问题。
【解决方案2】:

迭代器的作用类似于指针,因此为了调用存储为 STL 容器中指针的对象的成员函数,您需要取消引用两次:

std::list<Actor*>::iterator iter = actorList.begin();
(*iter)->getActorId();

或者:

(**iter).getActorId();

【讨论】:

    【解决方案3】:

    可能多次调用erase的容器上的迭代器for循环是等待发生的灾难,因为erase通常至少会使传递给它的迭代器(以及指向已擦除元素的任何其他迭代器)无效,并且无效的迭代器不能被安全地增加。只调用一次擦除的循环可以使用“break;”在不使用无效迭代器的情况下退出 for 循环。

    Iterator invalidation rules

    正如我在此问题导致的多个分段错误一周后告诉我的同事一样,如果您想循环容器并调用擦除,请使用 while 循环并确保您获得下一项的有效迭代器(或 end())在你调用擦除之前。最简单的方法是在调用站点后递增迭代器。对于std::list::erase(iterator),也可以使用它的返回值作为新的迭代器值。

    list iterator not incrementable

    【讨论】:

    • 目前的情况并不是指在 for 循环中多次调用擦除“列表中的每个成员都有一个具有唯一 ID 的成员。”
    • 如果在一次擦除后没有调用 break,那么当重新进入 for 循环头部并且迭代器递增时,代码变得不符合标准并且可能会崩溃。
    • 迭代器是值,而不是引用。列表迭代器的一种合法实现是使用内部数据结构的地址作为迭代器值。好吧,在擦除之后,该内部数据结构被释放。通常,当您在 operator++ 期间引用死存储时,其中仍会包含有效数据……但并非总是如此……如果没有,则会出现灾难性错误。
    【解决方案4】:

    您必须使用迭代器,因为列表是一种顺序数据结构。 一旦有了迭代器,就可以向前移动并使用间接运算符*提取指向对象的指针

    list<Actor *>::iterator it = actorList.begin();
    Actor * innerPtr = *it;
    innerPtr->yourMethod();
    

    一旦你知道要删除的演员是一个演员,你可以使用方法erase(position);使用迭代器:

    if( innerPtr->getActorId() == <your condition> )
    {
        actorList.erase(it);
    }
    

    但是,如果您需要使用演员 ID 进行搜索,我建议您切换到不同的数据结构,例如关联容器(例如地图)。

    【讨论】:

      【解决方案5】:

      要找到你想要做某事的节点可以使用std::find_if():

      #include <algorithm>
      #include <list>
      using namespace std;
      
      
      class Actor{
      
          private:
          int id;
      
          public:
          int getActorID() const { return id;};
      };
      
      
      // a functor used for pre-C++11 since lambdas aren't supported
      struct isActor
      {
      private:
           int target;
      
      public:
          isActor( int target) : target(target) {}
      
          bool operator()( Actor const* pa) const
          {
              return pa->getActorID() == target;
          }
      };
      
      
      std::list<Actor *> actorList;
      
      std::list<Actor *>::iterator i;
      
      int main()
      {
          // pre-C++11 technique
          i = std::find_if( actorList.begin(), actorList.end(), isActor(42));
      
          // C++11 lambda technique
          int id = 42;
          i = std::find_if( begin(actorList), end(actorList), [=](Actor const* pa) {
              return pa->getActorID() == id;
          });
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-07
        • 2015-08-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多