在 C++ 中,从列表中删除对象

发布于 2024-09-27 20:53:18 字数 543 浏览 10 评论 0原文

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

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

I'm writing a program which is more or less like this :

#include <list>

list<MyClass> things;

class MyClass {
   // some stuff

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

What do I need to write instead of THING_IS_ME?

In other words, I'm using a global STL list as a collection of things. At some point, an object which is in the list recognises that it is redundant and wants to a) get itself removed from the list, and b) get itself destructed.

How do I do this?

I haven't written C++ for about 15 years and am a bit confused by this page here : http://www.cplusplus.com/reference/algorithm/remove_if/

What are these Predicates? Does C++ have higher-order functions now?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

感性 2024-10-04 20:53:19

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

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

  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());

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

Things have changed dramatically in C++ in the past 15 years. In July 1994 Alexander Stepanov's proposal of a library that incorporates his ideas of generic programming received the final approval from the ANSI/ISO committee. This library that we conveniently call today the STL subsequently became a standard C++ library. The story of the STL is about as fascinating as the ideas behind it, and it definitely is worth the read.

The std::remove_if() function that you found is just another reflection of this philosophy that became part of C++'s modern identity. In short, this is a generic function that will work on any container (sequence) of elements and with any(thing that acts like a) condition. To this effect, you must provide the function two things:

  1. a couple of iterators that delimit the range of elements you wish to work on;
  2. and a predicate that, when invoked on an element, returns true if the element is to be removed and false otherwise.

As it turns out, in this case the predicate you want is that of equality. And because it is such a common task to remove elements based on equality the standard also provides the std::remove() function, which assumes an implicit equality predicate. Of course, you must make sure that elements can compare:

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

We can then use our predicate to remove elements of type MyClass:

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

Recall that the standard function std::remove() works on any container, even ones that haven't been created yet. Because every kind of container has its own way of removing elements, this function can't really perform the removal without knowing implementation details of the container it works on. So instead, the std::remove() function swaps elements around such that the elements "removed" are at the end of the container. Then, it returns an iterator pointing at the first element of the consecutive elements "removed".

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

Finally, we truly remove elements by calling the specific container's removal function, which works on a single position in the list or on a range of consecutive elements to remove:

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

It is not uncommon to see this sort of code in a single line:

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

This all may seem overwhelming and complicated, but it has a few advantages. For one thing, this design of the standard library supports dynamic programming. It also allows the standard library to offer containers with very slim interfaces, as well as few free functions that work on many different kinds of containers. It allows you to quickly create a container and instantly gain all the features of the standard library to work with it. Alternatively, it allows you to quickly write a generic function that instantly works with all standard containers -- those written already and those not written yet.

固执像三岁 2024-10-04 20:53:19

(最初是一组注释,但在发现 OP 实际想要做什么后重写为答案。)

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

如果拥有 MyClass 的副本没有意义,那么您最好使用 指针容器

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

(Originally a set of comments, but rewritten as an answer after discovering what the OP actually wanted to do.)

You do realize that the STL containers store copies of things you insert, right? That means instances of MyClass better be comparable (e.g. via operator==) - you can't just compare the addresses as they will always be different.

If having copies of MyClass makes no sense, then you're probably better off with a pointer container.

That being said, the C++ language uses copy-semantics by default. The language requires that you make things like references explicit in the code. I highly recommend that you pick up a good C++ book or you will be tripped up by issues like this in the future.

っ〆星空下的拥抱 2024-10-04 20:53:19

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

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。这是一个非常重要的惯用法,擦除删除惯用法,必须学习。

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

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

First, a simple hand-written loop:

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

Second, using the the remove_if algorithm. remove_if being an algorithm, as opposed to a member function of list, can't actually delete any elements, rather it moves the elements-to-be-deleted towards the end of list. Subsequently erase has to be called. This is a very important idiom, erase-remove idiom, that must be learnt.

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

where deletionPredicate is a function or function-object that takes a single argument of type and returns bool. The elements for which true is returned are considered to be removed.

吝吻 2024-10-04 20:53:19

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

链接到的页面的示例中,谓词是执行以下操作的代码行:说

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

要让您的代码执行您想要的操作,您需要编写如下内容:

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

并且您将像这样定义 SomePredicateFunction (用适当的测试替换 true ) :

bool SomePredicateFunction (MyClass c) { return true; }

The remove_if function takes three parameters: two iterators that define the range you're working on, and a predicate (test function that returns bool). The start iterator points to the first element you want to work on; the end iterator points to the element after the last element in the range.

In the example from the page you linked to, the predicate is the line of code that says

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

To make your code do what you want, you'd need to write something like this:

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

and you'd define SomePredicateFunction like so (replacing true with an appropriate test):

bool SomePredicateFunction (MyClass c) { return true; }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文