【问题标题】:In C++, removing an object from a list在 C++ 中,从列表中删除一个对象
【发布时间】:2010-10-20 01:27:01
【问题描述】:

我正在编写一个或多或少像这样的程序:

#include <list>

list<MyClass> things;

class MyClass {
   // some stuff

   void remove() {
       things.remove_if(THING_IS_ME);
   }
};

我需要写什么来代替 THING_IS_ME?

换句话说,我使用全局 STL 列表作为事物的集合。在某些时候,列表中的对象意识到它是多余的,并希望 a) 将自己从列表中删除,并且 b) 将自己销毁。

我该怎么做?

我已经有 15 年没有编写 C++ 了,对这里的这个页面有点困惑:http://www.cplusplus.com/reference/algorithm/remove_if/

这些谓词是什么? C++ 现在有高阶函数了吗?

【问题讨论】:

  • 啊。不,我完全没有意识到!!!我只是假设它会存储对我放入其中的任何内容的引用。已经 15 年了……我想我还在用 Java 思考。 :-( 谢谢
  • 您确实意识到 STL 容器存储您插入的内容的副本,对吗?这意味着MyClass 的实例更好地具有可比性(例如通过operator==) - 您不能只比较地址,因为它们总是不同的。如果拥有MyClass 的副本没有意义,那么使用a pointer container 可能会更好。
  • @interstar:(对不起,我在你回复之前删除了我的评论。)是的,C++ 语言默认使用复制语义。该语言要求您在代码中明确引用之类的内容。如果拥有MyClass 的副本没有意义,您可以使用a pointer container。我强烈建议您选择a good C++ book,否则您将来会被此类问题绊倒。
  • 我想重点是,我真的应该明确地创建我的列表,然后只在其中存储指针。然后我可以使用 remove(this) 来比较指针值?
  • @interstar:是的,可以。请记住,如果您这样做,您必须处理任何内存管理问题。例如,您必须考虑对象生命周期以及 std::list 不会自动从容器中删除的 delete 指针这一事实。这就是我推荐 a pointer container 的原因,它会为您解决这个问题。

标签: c++ stl remove-if


【解决方案1】:

在过去的 15 年里,C++ 发生了巨大的变化。 1994 年 7 月,Alexander Stepanov 提出的包含他的泛型编程思想的库的提议获得了 ANSI/ISO 委员会的最终批准。这个我们今天方便地称为 STL 的库随后成为标准 C++ 库。 STL 的故事与其背后的想法一样引人入胜,绝对值得一读。

您发现的std::remove_if() 函数只是这一理念的另一种体现,它已成为 C++ 现代身份的一部分。简而言之,这是一个通用函数,适用于任何元素容器(序列)和任何(行为类似于 a)条件。为此,您必须为函数提供两件事:

  1. 几个迭代器,用于界定您希望处理的元素范围;
  2. 和一个 谓词,当在元素上调用时,如果要删除该元素,则返回 true,否则返回 false。

事实证明,在这种情况下,您想要的谓词是相等。由于基于相等性删除元素是一项常见任务,因此该标准还提供了std::remove() 函数,该函数假定隐式相等谓词。当然,你必须确保元素可以比较:

bool operator==(const MyClass& a, const MyClass& b)
{
    // return true if the two are equal, and false otherwise.
}

然后我们可以使用我们的谓词删除MyClass类型的元素:

std::remove(things.begin(), things.end(), *this);  // if *this == elem

回想一下,标准函数std::remove() 适用于任何 容器,即使是尚未创建的容器。因为每种容器都有自己的移除元素的方式,所以如果不知道它所处理的容器的实现细节,这个函数就无法真正执行移除。所以取而代之的是,std::remove() 函数交换元素,使得“删除”的元素位于容器的末尾。然后,它返回一个迭代器,指向被“移除”的连续元素中的第一个元素。

typedef std::list<MyClass>::iterator iter;
iter first_removed = std::remove(things.begin(), things.end(), *this);

最后,我们通过调用特定容器的删除函数来真正删除元素,该函数作用于列表中的单个位置或一系列要删除的连续元素:

things.erase(first_removed, things.end());

在一行中看到这样的代码并不罕见:

things.erase(std::remove(things.begin(), things.end(), *this),
             things.end());

这一切可能看起来势不可挡和复杂,但它有一些优点。一方面,标准库的这种设计支持动态编程。它还允许标准库提供具有非常苗条接口的容器,以及适用于许多不同类型容器的少量免费函数。它允许您快速创建一个容器并立即获得标准库的所有功能来使用它。或者,它允许您快速编写一个通用函数,该函数可以立即与所有标准容器一起工作——那些已经编写的和尚未编写的。

【讨论】:

    【解决方案2】:

    (最初是一组 cmets,但在发现 OP 真正想要做什么后重写为答案。)

    您确实意识到 STL 容器存储您插入的内容的副本,对吗?这意味着MyClass 的实例更好地具有可比性(例如通过operator==) - 您不能只比较地址,因为它们总是不同的。

    如果拥有 MyClass 的副本没有意义,那么您最好使用a pointer container

    话虽如此,C++ 语言默认使用复制语义。该语言要求您在代码中明确引用之类的内容。我强烈建议您选择a good C++ book,否则您将来会被此类问题绊倒。

    【讨论】:

    • 好的。我将此标记为我想要的答案,因为它确实是我需要的答案。即使这不是我在问题中要求的。 IE。我需要在列表中显式使用指针并进行普通删除。 (至少在我进入 Boost 之前。)
    【解决方案3】:

    首先,一个简单的手写循环:

    for( list<MyClass>::iterator it = things.begin(); it != things.end(); /*  */ ) {
        if( qualifiesForDelete( *it ) ) {
            it = things.erase( it );
        }
        else {
            ++it;
        }
    }
    

    其次,使用remove_if 算法。 remove_if 是一种算法,与list 的成员函数相反,它实际上不能删除任何元素,而是将要删除的元素移向列表末尾。随后必须调用erase。这是一个非常重要的成语,erase-remove idiom,必须学习。

    things.erase( 
        remove_if( things.begin(), things.end(), deletionPredicate ), 
        things.end() 
    );
    

    其中deletionPredicate 是一个函数或函数对象,它接受一个类型的参数并返回bool。为其返回true 的元素被视为已删除。

    【讨论】:

      【解决方案4】:

      remove_if 函数接受三个参数:两个定义您正在处理的范围的迭代器和一个谓词(返回 bool 的测试函数)。开始迭代器指向您要处理的第一个元素;结束迭代器指向范围内最后一个元素之后的元素。

      page you linked to 的示例中,谓词是表示的代码行

      bool IsOdd (int i) { return ((i%2)==1); }
      

      要让你的代码做你想做的事,你需要这样写:

      things.remove_if(things.begin(), things.end(), SomePredicateFunction);
      

      你会像这样定义SomePredicateFunction(用适当的测试替换true):

      bool SomePredicateFunction (MyClass c) { return true; }
      

      【讨论】:

        猜你喜欢
        • 2018-07-08
        • 1970-01-01
        • 1970-01-01
        • 2021-07-11
        • 2020-11-07
        • 1970-01-01
        • 2016-11-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多