Effective STL Terms 32

zhaozj2021-02-11  203

Terms 32: If you really want to delete something, just after the REMOVE algorithm is connected.

I will start this clause from Remove's review, because Remove is the most confusing algorithm in STL. Misunderstanding Remove is easy to disperse all about REMOVE behavior - why it does this, how it is doing it - it is very important.

This is the Remove statement:

Template

ForwardItemrator Remove (ForwardItemrator First, ForwardItemrator Last,

Const T & Value;

Just like all algorithms, REMOVE receives a pair of iterators that specify the element interval it operate. It does not receive a container, so Remove does not know which container it acts. In addition, REMOVE is also impossible to find containers because there is no way to obtain containers corresponding to it from an iterator.

Think how to remove an element from the container. The only way is to call a member function of that container, almost all of the ERASE, (List There are several member functions of the element that are not called ERASE, but they are still a member function.) Because unique from the container Element method is to call a member function on that container, and because Remove cannot know that it is operating, it is impossible to remove elements from one container. This explains another frustrating perspective - from the REMOVE element from one container does not change the number of elements in the container:

Vector v; // Establish a vector to populate it with 1-10

v.reserve (10); // (call reserve intervene in terms 14)

For (INT i = 1; i <= 10; i) {

v.push_back (i);

}

Cout << v.size (); // Print 10

v [3] = v [5] = v [9] = 99; // Set 3 elements 99

REMOVE (v.begin (), v.end (), 99); // Delete elements equal to 99

Cout << v.size (); // is still 10!

To make this example, remember the following sentence:

Remove does not "really" delete something because it can't do it.

Repeat your benefits:

Remove does not "really" delete something because it can't do it.

Remove doesn't know which container is to delete something, without a container, it has no way to call member functions, and if "really" wants to delete something, it is necessary.

The above explains what REMOVE does not do, and explains why it does not do. What we need to review now is that Remove has done.

Very briefly, Remove moves the element in the specified interval until all "non-deleted" elements begin at the beginning of the interval (the same position, the same as that of them). It returns an iterator that pointing the next "non-deleted" element. The return value is the "new logical end point" in the interval.

For example, this is V looks like before calling Remove:

If we store the REMOVE return value in a new iterator called Newend:

Vector :: Iterator New (Remove (V.Begin (), V.End (), 99));

This is the way to call V appearance:

Here I use the question mark to indicate the values ​​of those that have been deleted from V, but continue to exist.

If the "non-deleted" element is between V.Begin () and Newend, "deleted" element must be between NEWEND and V.END () - it seems reasonable. In fact, not this! The "deleted" value does not have to exist again in V. Remove did not change the order of the elements in the interval, so it won't put all "deleted" elements to end and arrange all "non-deleted" values ​​at the beginning. Although the standard has no requirements, in general, the elements after the new logical end point will remain in their original values ​​in the section. After calling Remove, in all the implementations I know, V look like this: As you can see, the two "99" that I have ever existed is no longer there, and a "99" still exists. In general, after calling Remove, the value deleted from the interval may not continue to exist in the interval. Most people think this is very strange, but why? You ask REMOVE to remove some value, so it is done. You didn't ask it to put the deleted value on a specific location you can get in the future, so it didn't do it. Is there a problem? (If you don't want to lose any value, you may call Partition instead of remove, Partition is described in Terms 31.)

Remove's behavior sounds very disgustable, but it is just an accompanying result of the algorithm. Internally, Remove traversed this interval, cover the value to "delete" to the value you want to keep later. This coverage is completed by assigning elements that holds covered values.

You can imaging that REMOVE has completed a compression, the deleted value performs the role of the hole filled in the compression. For our Vector V, it follows the following:

Remove detection V [0], found that its value is not to be deleted, then move to V [1]. The same situation occurs in V [1] and V [2]. V [3] should be deleted, so it records the value of V [3] should be overwritten, then it moves to V [4]. This similar record V [3] is a "hole" that needs to be filled. The value of V [4] should be held, so it assigns V [4] to V [3], records V [4] should be overwritten, then move to V [5]. Continue similar compression, it uses V [4] "Fill" V [3] and records V [4] is now a hole. It is found that V [5] should be deleted, so I ignore and it moves to V [6]. Still remembering V [4] is a hole waiting for the filler. It is found that V [6] is a value that should be retained, so impart V [6] to V [4]. Remember V [5] is now the next hole to be filled, then move to V [7]. In a sense, it is similar to, Check V [7], V [8], and V [9]. Assign V [5], V [8] to v [6], ignore V [9], because the value of V [9] is to be deleted. Returns the iterator that specifies the next element to be covered, this element is V [7] in this example.

You can expect the movement of the V value in V like this:

As explained in Terms 33, it is important to have an important influence when Remove is overwritten when it is deleted. But for this clause, Remove does not remove any elements from the container because it is not able to do it. Only the container member function can remove the container element, and that is the entire point of this Terfection: If you really want to delete something, you should pick up the ERASE back in Remove.

Elements you want ERASE are easy to identify. They are elements from the "new logical end" from the interval to continue to the original interval of the real end point. To remove those elements, all the things you have to do is to call the ERASE's interval form (see Terms 5). Because Remove itself is convenient to return an iterator of the new logical end point of the interval, this call is straightforward:

Vector v; // is as before

v.ras (transove (v.begin (), v.end (), 99), v.end ()); // Really removes all / / equal to 99 elements

Cout << v.size (); // now return 7

The first parameter of the REMOVE is very common as the ERASE section. This is a usual method. In fact, Remove and ERASE are intimate alliances, which are integrated into the List member function Remove. This is the only function of the only name called Remove and removes the element from the container:

List li; // Create a list

// put some value into

Li.Remove (99); // Remove all elements equal to 99:

// Really delete the element,

// So it's size may change

Frankly, calling this REMOVE function is a contradiction in STL. Similar functions in the associated container called ERASE, List's Remove can also be called ERASE. But it doesn't, so we must get used to it. The world we are not all possible worlds, but it is what we are. (Attach, Terms 44 pointed out that for List, calling the Remove member function is more efficient than applying ERASE-REMOVE usa.)

Once you know that Remove can't "really" remove things from a container, combined with ERASE will become a matter of course. The only thing you have to remember is that Remove is not the only algorithm. There are two "similar REMOVE" algorithms: remove_if and unique.

The similarities between REMOVE and REMOVE_IF are straightforward. So I will not let's talk, but the unique behavior is like REMOVE. It is used to delete something from a range (adjacent repeat value) without accessing a container holding interval elements. As a result, if you really want to delete an element from the container, you must also call Unique and ERASE, Unique is similar to REMOVE in LIST. Just like List :: remove really deletes something (and more efficient than Erase-Remove usual). List :: UNIQUE also deletes adjacent repeat values ​​(also efficient than Erase-Unique).

转载请注明原文地址:https://www.9cbs.com/read-4775.html

New Post(0)