Terms 30: Make sure the target interval is large enough
The STL container automatically expands them to accommodate new objects when they are added (via INSERT, PUSH_FRONT, PUSH_BACK, etc.). This work is very good, some programmers are paralyzed because of this faith, think they don't have to worry about vacating the objects in the container, because the container can take care of these. If so, just fine! The problem appeared when the programmer wanted to insert the object in the container but did not tell STL. This is a common method of self-performance:
Int Transmogrify (int x); // This function is from X
// Produce some new value
Vector
... // put the data into values
Vector
TRANSFORM (Values.Begin (), Values.end (), // VALUES Each object
Results.end (), // Turn this VALUES
TRANSMOGRIFY); // Attached to Results
// This code has bugs!
In this example, the Transform is informative interval starting from Results.end (), so that it is where to start writing the results of Transmogrify on each element of VALUES. Just like all the algorithms that use the target interval, Transform writes the result by assigning the elements of the target interval, Transform is applied to Values [0] and assigns * results.end (). Then it will apply Transmogrify to Value [1] and assign the results to * (results.end () 1). That can only bring disasters, because there is no object in * results.end () * (results.end () 1) is not! Calling Transform is incorrect because it assigns an object that does not exist. (Terms 50 explain how a debugging implementation of STL detects this problem in operation period.) Programmer who made this error almost always thought that the results of their call algorithm can be inserted into the target container. If that is what you want, you must say it. STL is a library, not a spirit. In this example, "Please put the result of Transform's results in the end of the RESULTS container" to call back_inserter to generate an iterator that specifies the starting point of the target interval:
Vector
Values.Begin (), Values.end (), each object in // Values,
Back_INSERTER (RESULTS), // End of RESULTS
Transmogrify); // Insert the returned VALUES
Internally, the iterator returned by back_inserter calls PUSH_BACK, so you can use Back_Iserter on any container that provides PUSH_BACK (any standard sequence container: Vector, String, Deque, and List). If you want an algorithm to insert something on the front of the container, you can use Front_Inserter. Inside, Front_inserter uses PUSH_FRONT, so Front_Insert only and provides the container that provides that member function (which is devE and list):
... //
List
Transform (Values.Begin (), VALUES.END (), // In the RESULTS front-end Front_insert (Results), //
Transmogrify); // Insert the result of Transform
Because Front_Insert is added with PUSH_FRONT to add each object to the object order in Results, in contrast of the corresponding object sequence in Values. This is also one of the reasons why Front_Inserter has no back_inserter. Another reason is that VECTOR does not provide PUSH_FRONT, so Front_INSERTER cannot be used for Vector.
If you want Transform to put the output result at the front end of the Results, you also want to output the corresponding object order in VALUES, as long as it is iterated in the opposite order. VALUES:
List
Values.Rbegin (), Values.rend (), // at the front end of the Results
Front_inserter (Results), // Insert Transform
Transmogrify); / / Keep the relative object order
Front_inserter allows you to force the algorithm into the results of the front end of the container, back_inserter lets you tell them to put the result in the backend of the container, a bit amazing thing is that INSERTER allows you to force the algorithm to insert their results into any location in the container:
Vector
...
Vector
... // Before calling Transform
// Results already have some data
Transform (Values.Begin (), Values.end (), // Putting Transmogrify
Inserter (Results, Results.begin () Results.Size () / 2), / / Result Insert
TRANSMOGRIFY); // Results
Whether you use Back_Inserter, Front_Inserter or Insert, each time the insertion of the destination interval completes an object. Terms 5 explains that this may be expensive for continuous memory containers (VECTOR, STRING, and Deque), but the recommendations of Terms 5 (using interval member functions) cannot be applied to the case where the algorithm is used to complete the insert. In this case, Transform will write a value each time you write, you can't change.
When the container you want to be inserted is a Vector or String, you can minimize this cost by the recommendations of the Terms 14, pre-calling Reserve. You still have to withstand the overhead of moving elements every time, but at least you avoid the intrinsic memory of the reassigned container:
Vector
Vector
...
Results.Rserve (Results.Size () VALUES.SIZE ()); / / Determine Results at least
/ / Can also be installed
// VALUES.SIZE () Element
VALUES.EGIN (), Values.end (), //
INSERTER (RESULTS.SIZE () RESULTS.SIZE () / 2), //, RESULTS
TRANSMOGRIFY); / / no redistribution operation
When using RESERVE to increase the efficiency of a series of inserts, always remember that the RESERVE only increases the capacity of the container: The size of the container still has not changed. Even if you call RESERVE, when you want the container to add new elements to Vector or String, you must also use insert iterators (such as one of the iterators returned from back_inserter, front_inserter or inserter). It is necessary to make these very clearly, here there is an error method for increasing the efficiency of the example at the beginning of this Territory (that is, the results of the data of the data in the values of the values to Results):
Vector
Vector
...
Results.Rserve (results.size () value ()); //
Transform (Values.Begin (), Values.end (), // Write the result of Transmogrify
Results.end (), // Directation of memory
Transmogrify); // Behavior is undefined!
In this code, Transform happily attempts to assign the original and unmelted memory assignment of the RESULTS tail. Typically, this will result in an operation error because it makes sense only between the two objects, not between an object and the original bits. Even if this code happens to do things you want to do, RESULTS will not know that Transform is "created" on its unused capacity. It is still the same as the original when recalling Transform until the resultS knows. Similarly, its END iterator still points to the same location before transferring Transform. Conclusion? Using Reserve without using an inserted iterator, it will cause undefined behavior inside the algorithm, and it will also disappear.
The method of correctly writing this example is to use Reserve and insert iterators:
Vector
Vector
Results.Rserve (results.size () value ()); //
Transform (Values.Begin (), Values.end (), // Put the results of Transmogrify
Back_inserter (results), // Write the end of Results,
Transmogrify); // Avoid redistribution when processing
So far, I have assumed that you let the algorithms like Transform as new elements into the container. This is the usual expectations, but sometimes you have to cover the elements of the existing container rather than inserting new. When this situation, you don't need to insert iterators, but you still need to make sure your destination is large enough in accordance with the recommendations of this Terms.
For example, suppose you make Transform to override the Elements of Results. It is simple if it has the same elements as Values as Values. If not, you must also use resize to make sure it has.
Vector
Vector
...
if (Results.Size () Results.resize (Values.size ()); // and values } Transform (Values.Begin (), Values.end (), // overrides Values.Size () Elements of Results.begin (), // Results Transmogrify); Or you can empty Results and use insert iterators in the usual way: ... Results.clear (); // Destroy Results // All elements Results.reserve (Values.size ()); // Keep enough space Values.Begin (), Values.end (), // returns the TRANSFORM Pack_INSERTER (RESULTS), // Add RESULTS Transmogrify); This Terms argue a lot of changes in this topic, but I hope that you can keep in mind. Whenever you use a algorithm that requires a designated destination interval, make sure the destination interval is large enough or can increase when the algorithm is executed. If you choose to increase the size, use the insert iterator, such as ostream_iterators, or returned from back_inserter, front_inserter, or inserter. This is something you need to remember.