1

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

#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++现在有高阶函数吗?

4

4 回答 4

5

在过去的 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.
}

然后我们可以使用我们的谓词来删除 type 的元素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());

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

于 2010-10-20T02:50:54.430 回答
2

(最初是一组评论,但在发现 OP 真正想要做的事情后重写为答案。)

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

如果拥有 MyClass 的副本没有意义,那么使用指针容器可能会更好。

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

于 2010-10-20T02:00:26.823 回答
0

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

您链接到的页面的示例中,谓词是代码行

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

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

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

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

bool SomePredicateFunction (MyClass c) { return true; }
于 2010-10-20T02:09:16.323 回答
0

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

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 成语,必须学习。

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

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

于 2010-10-20T01:37:10.490 回答