1

如果对象的属性之一与条件匹配,我正在尝试从对象列表中删除一个元素。这是我这样做的功能,但是,在执行此操作然后打印内容之后,erase() 似乎没有任何效果。我在这里做错了什么?

void FileReader::DeleteProcess(int id, list<Process> listToDeleteFrom)
{
    list<Process>::iterator process;

    for(process = listToDeleteFrom.begin(); process != listToDeleteFrom.end(); process++)
    {
        if (process -> ID == id)
        {
            listToDeleteFrom.erase(process);
        }
    }
}
4

3 回答 3

11

首先,您需要通过引用传递列表;您的代码正在处理副本,因此它所做的更改不会影响调用者的列表:

void FileReader::DeleteProcess(int id, list<Process> & listToDeleteFrom)
                                                     ^

其次,擦除列表元素会使引用该元素的任何迭代器无效,因此尝试在之后进行迭代将导致未定义的行为。如果只删除一个元素,则在调用erase;后直接从函数返回 否则,循环需要构造如下:

for (auto it = list.begin(); it != list.end(); /* don't increment here */) {
    if (it->ID == id) {
        it = list.erase(it);
    } else {
        ++it;
    }
}
于 2012-10-01T02:31:25.027 回答
6

erase()在迭代器迭代时调用list会使迭代器无效。将要擦除的元素添加到第二个列表,然后将其删除。

另请注意,您是按值传递列表,而不是使用引用或指针。您的意思是使用list<Process>& listToDeleteFromorlist<Process>* listToDeleteFrom吗?

于 2012-10-01T02:27:13.343 回答
1

您看不到任何更改的原因是您的列表没有通过引用传递,因此您只是从列表的副本中删除元素。

将其更改为:

void FileReader::DeleteProcess(int id, list<Process> &listToDeleteFrom) //note &

这将在函数中保持相同的语法并修改原始语法。

但是,您删除元素的方式有点次优。如果您有 C++11,以下内容将消除您的失效问题,并且使用为该工作设计的现有算法更加惯用:

listToDeleteFrom.erase ( //erase matching elements returned from remove_if
    std::remove_if( 
        std::begin(listToDeleteFrom), 
        std::end(listToDeleteFrom), 
        [](const Process &p) { //lambda that matches based on id
            return p->ID == id;
        }
    ),
    std::end(listToDeleteFrom) //to the end of the list
);

请注意保留std::list<>::erase在那里以实际删除匹配的元素。这被称为擦除删除习语。

于 2012-10-01T02:35:13.403 回答