C++ 如何删除两个不同 std::lists 中引用同一元素的指针?

C++ How can I remove pointers refering to the same element in two different std::lists?

我是 C++ 编程新手,所以如果我的源代码不是很出色,请不要生我的气。

为了我的研究,我必须编写一个程序来处理图中的节点和边。

我的源代码中有 2 个 std::lists。第一个用于存储一般 Nodes,另一个用于保存我称为 ArticleNodes 的 class 节点类型。一般来说,所有的元素都是指向创建对象的指针。

为了确定一个对象在另一个列表中是否相同,我保存了内存地址并将其与第二个列表中的元素进行比较。如果匹配,第二个元素将被删除。

现在我想删除两个列表中的一个元素:

void Graph::deleteNode(unsigned int nodeNumber)
{
    list<Node*>::iterator it = m_nodes.begin();
    ArticleNode* pCurrentArticleNode;
    for(unsigned int i=1; i<nodeNumber; i++) { it++; }

    Node* pCurrentNode = (*it);

    for (list<ArticleNode*>::iterator itArticle = m_articlenode.begin(); itArticle != m_articlenode.end(); itArticle++)
    {
        pCurrentArticleNode = (*itArticle);
        if(pCurrentNode==pCurrentArticleNode) { m_articlenode.remove(pCurrentArticleNode); }
    }

    m_nodes.remove(pCurrentNode);

    delete pCurrentNode;
    delete pCurrentArticleNode;
}

我可以编译它,但是当我调用该函数时,我的程序只是以 return 1 退出。 实际上,我发现 if 子句中的 remove-command 是问题所在。为什么那不起作用??

您必须使用 std::listerase 方法才能在遍历列表时从列表中删除元素。

这应该可以解决问题:

void Graph::deleteNode(unsigned int nodeNumber)
{
  list<Node*>::iterator it = m_nodes.begin();
  ArticleNode* pCurrentArticleNode;
  for(unsigned int i=1; i<nodeNumber; i++) { it++; }

  Node* pCurrentNode = (*it);
  list<ArticleNode*>::iterator itArticle = m_articlenode.begin();

  while(itArticle != m_articlenode.end()) {
    pCurrentArticleNode = (*itArticle);
    if(pCurrentNode==pCurrentArticleNode) {
      m_articlenode.erase(itArticle++);
    } else {
      itArticle++;
    }
  }

  m_nodes.remove(pCurrentNode);

  delete pCurrentNode;
  delete pCurrentArticleNode;
}

您的代码中的一个简单问题是,如果您进入 if 条件得到满足并且它的主体被执行,您应该退出循环。 std::remove 使迭代器无效,您将在下一次迭代中遇到问题,所以这样做:

for (list<ArticleNode*>::iterator itArticle = m_articlenode.begin(); itArticle != m_articlenode.end(); itArticle++)
    {
        pCurrentArticleNode = (*itArticle);
        if(pCurrentNode==pCurrentArticleNode) 
        { 
             m_articlenode.remove(pCurrentArticleNode);
             break;
        }
    }

总的来说,代码中还有其他问题。作为第一步,我建议使用 shared_ptr 来管理您的节点,并使用 shared_ptr 列表而不是指针列表。

当您使用 remove( ) 方法从 std::list 对象中删除一个元素时,所有指向该元素的迭代器都会失效。在您的例子中,从列表 m_articlenode 中删除一个元素后,迭代器对象 itArticle 变得无效。当你增加那个迭代器时,你会得到一个未定义的行为。

注意 remove( ) 方法会删除列表中具有给定值的所有项目。所以你根本不需要 for 循环。这是您函数的固定版本:

void Graph::deleteNode(unsigned int nodeNumber)
{
    list<Node*>::iterator it = m_nodes.begin();
    for(unsigned int i=1; i<nodeNumber; i++) { it++; }

    Node* pCurrentNode = (*it);

    m_articlenode.remove(pCurrentNode);
    m_nodes.remove(pCurrentNode);

    delete pCurrentNode;
}

你应该更多地使用算法而不是手动完成所有事情:

void Graph::deleteNode(unsigned int nodeNumber)
{
    assert (nodeNumber < m_nodes.size());

    auto it = std::next( m_nodes.begin(), nodeNumber - 1 );

    auto itArticle = std::find( m_articlenode.begin(), m_articlenode.end(), *it );
    if( itArticle != m_articlenode.end() )
        m_articlenode.erase( itArticle );

    delete *it;
    m_nodes.erase(it);
}

顺便说一句,您的代码删除了同一个对象两次。

I am new in programming c++, so please don't be angry with me if my source code is not exactly brilliant.

我们都从某个地方开始。

Now I'd like to delete one element in both lists:

好的。快速提问。如果文章节点已经被删除(通过基本节点),为什么要删除它?我假设现在节点在列表中没有重复:

我的解决方案如下...我已将列表作为参数传递。查看评论:

#include <list>
#include <algorithm>

struct Node
{
  virtual ~Node(){} //For dyna cast to work...
};

struct ArticleNode : Node
{
};

void deleteNode(std::list<ArticleNode*>& articleList, std::list<Node*>& m_nodes, unsigned int nodeNumber)
{
    using namespace std;

    if (m_nodes.size() > nodeNumber)
    {
      auto it = m_nodes.begin();
      // Advance advances our iterator by N. No need for your for loop - less risk...
      std::advance(it,nodeNumber);
      Node* currentNode = *it;

      //Casting is bad here, but hey, lets assume if type is wrong, we only erase
      // it from Node...(Your call)?
      ArticleNode* currentArticleNode = dynamic_cast<ArticleNode*>(currentNode); 

      if (currentArticleNode) 
      {
        //Use find here.... KISS
        auto foundPos = std::find(articleList.begin(), articleList.end(), currentArticleNode);
        if (foundPos != articleList.end())
        {
          //No need to delete currentArticleNode, as we're deleting it already...
          articleList.erase(foundPos);
        }
        //Assuming only one item for now...
      }
      //Else our node was obviously not the right type, and cannot exist in articleNodes...

      m_nodes.erase(it);
      delete currentNode;
    }
    else
    {
      std::cout << "No such node: " << nodeNumber << std::endl;
    }
}