Introduction to the standard template library (STL) (I)
This article uses the list container as an example to introduce the basic content of STL, from the container to the iterator, and then to the common functions. The examples are rich and easy to understand. It is an entry-level article of STL. You cannot miss it!
This article is about a new extension of the C ++ language-standard template library (STL.
When I was writing an article about STL for the first time, I had to admit that I underestimated the depth and breadth of the topic. There are a lot of contents to cover, and there are also a lot of books to describe STL in detail. So I thought about my original ideas again. Why should I write this article and contribute? How can this be used? Is there any need for another article on STL?
When I opened the musser and saini pages, I saw that the programming age had melted in front of me. I can see that it disappears late at night and the target software project appears. I see the maintained code. A year later, software I wrote using STL is still very easy to maintain. It is surprising that others can maintain well without me!
However, I also remember that it was difficult to understand the technical terms at the beginning. Once I bought musser & saini, everything appeared in sequence, but before that, what I desire most was some good examples.
At the beginning, stroustrup, as part of C ++, was not available yet, and it covered STL.
Therefore, I want to write an article about the real life of an STL programmer. If I have some good examples, especially new questions like this, I will learn faster.
Another thing is that STL should be very useful. Therefore, in theory, we should be able to start using STL immediately.
What is STL? STL is the standard template library, the standard template library. This may be the most boring term for the most exciting tool in history. Basically, STL is a collection of "containers", which include list, vector, set, and map. STL is also a collection of algorithms and other components. The collection of containers and algorithms here refers to the masterpiece of many smart people in the world for many years.
The purpose of STL is to standardize components so that you do not need to re-develop them. You can only use these ready-made components. STL is now part of C ++, so no additional installation is required. It is built into your compiler. Because STL list is a simple container, I plan to introduce how to use STL from it. If you understand this concept, there will be no other problems. In addition, the list container is quite simple and we will see this.
In this article, we will see how to define and initialize a list, calculate the number of its elements, find elements from a list, delete elements, and some other operations. To do this, we will discuss two different algorithms. General STL algorithms can operate on more than one container, while List member functions are proprietary operations of list containers.
This is a concise outline of three main STL components. STL containers can save objects, built-in objects, and class objects. They securely Save the object and define the interface of the object that we can operate on. The eggs on the egg won't roll to the table. They are safe. Therefore, objects in STL containers are safe. I know this metaphor sounds very old, but it is correct.
STL algorithms are standard algorithms. We can apply them to objects in those containers. These algorithms have well-known execution features. They can sort, delete, count, and compare objects, find special objects, merge them into another container, and perform other useful operations.
STL iterator is like a pointer to an object in a container. STL algorithms use iterator to operate on containers. Iterator sets the boundary of the algorithm, the length of the container, and other things. For example, some iterator only allows the algorithm to read elements, some allow the algorithm to write elements, and some allow both. Iterator also determines the processing direction in the container.
You can call the container member function begin () to obtain an iterator pointing to the starting position of a container. You can call the end () function of a container to obtain the last value of the past (that is, the value of the stopped value ).
This is all about STL, containers, algorithms, and iterator that allows algorithms to work on elements in containers. The algorithm operates the object in an appropriate and standard method, and the precise length of the container can be obtained through iterator. Once this is done, they will not "run out of the boundary ". There are also some other components with enhanced functionality for these core component types, such as function objects. We will see these examples. Now, let's take a look at the STL list.
Define a list
We can define an STL list as follows:
#include <string>#include <list>int main (void) { list<string> Milkshakes; return 0;}
You have defined a list. Simple? List <string> milkshakes is an instance of the List <string> template class, and an object of the class is instantiated. But don't rush to do this. In this step, you only need to know that you have defined a string list. You need to include the header file that provides the STL list class. I used GCC 2.7.2 to compile this test program on my Linux. For example:
g++ test1.cpp -o test1
Note that the header file iostream. H has been abandoned by the STL header file. This is why this is not found in this example.
Now we have a list. We can use it to install things. We will add a string to this list. A very important thing is the value type of list. The value type is the type of the object in the list. In this example, the Value Type of this list is string, because this list is used to put strings.
Use the List member functions push_back and push_front to insert an element to the list.
#include <string>#include <list>int main (void) { list<string> Milkshakes; Milkshakes.push_back("Chocolate"); Milkshakes.push_back("Strawberry"); Milkshakes.push_front("Lime"); Milkshakes.push_front("Vanilla"); return 0;}
We now have four strings in the list. The List member function push_back () puts an object behind a list, while push_front () puts the object in front. I usually put some error messages push_back () into a list, and then push_front () into a list, so that it will print it before the error message.
The List member function empty ()
It is important to know whether a list is empty. If list is empty, the empty () member function returns true. I usually use it like this. I use push_back () to put error messages in the list. Then, by calling empty (), I can tell whether the program has reported an error. If I define a list to put information, a warning, and a serious error, I can use empty () to easily tell whether a type of error has occurred.
I can sort these lists and use titles to sort them, or sort them into classes before printing them.
This is what I mean:
/*|| Using a list to track and report program messages and status */#include <iostream.h>#include <string>#include <list>int main (void) { #define OK 0 #define INFO 1 #define WARNING 2 int return_code; list<string> InfoMessages; list<string> WarningMessages; // during a program these messages are loaded at various points InfoMessages.push_back("Info: Program started"); // do work... WarningMessages.push_back("Warning: No Customer records have been found"); // do work... return_code = OK; if (!InfoMessages.empty()) { // there were info messages InfoMessages.push_front("Informational Messages:"); // ... print the info messages list, we'll see how later return_code = INFO; } if (!WarningMessages.empty()) { // there were warning messages WarningMessages.push_front("Warning Messages:"); // ... print the warning messages list, we'll see how later return_code = WARNING; } // If there were no messages say so. if (InfoMessages.empty() && WarningMessages.empty()) { cout << "There were no messages " << endl; } return return_code;}
Use a for loop to process elements in the list
We want to traverse a list, for example, print all objects in a list to see the results of different operations on the list. To traverse a list from an element to an element, we can do this:
/*|| How to print the contents of a simple STL list. Whew! */#include <iostream.h>#include <string>#include <list>int main (void) { list<string> Milkshakes; list<string>::iterator MilkshakeIterator; Milkshakes.push_back("Chocolate"); Milkshakes.push_back("Strawberry"); Milkshakes.push_front("Lime"); Milkshakes.push_front("Vanilla"); // print the milkshakes Milkshakes.push_front("The Milkshake Menu"); Milkshakes.push_back("*** Thats the end ***"); for (MilkshakeIterator=Milkshakes.begin(); MilkshakeIterator!=Milkshakes.end(); ++MilkshakeIterator) { // dereference the iterator to get the element cout << *MilkshakeIterator << endl; } }
This program defines an iterator, milkshakeiterator. We point it to the first element of the list. This can be done by calling milkshakes. Begin (). It returns an iterator pointing to the beginning of list. Then we compare it with the returned values of milkshakes. End (). When we get there, we stop.
The end () function of the container returns an iterator pointing to the last position of the container. When we get there, we stop the operation. We cannot ignore the return value of the end () function of the container. We only know that it means that the processing has reached the end of the container and should be stopped. This is required for all STL containers.
In the above example, every time we execute a for loop, we will repeat iterator to get the printed string.
In STL programming, we use one or more iterator in each algorithm. We use them to access objects in the container. To access a given object, we direct an iterator to it and then indirectly reference this iterator.
This list container, as you think, does not support adding a number in iterator to point to an object. That is to say, we cannot use milkshakes. Begin () + 2 to point to the third object in the list, because the STL list is implemented by a double-Chain List, which does not support random access. Vector and deque (vector and double-end Queue) and some other STL containers support random access.
The above program prints the content in the list. Anyone who reads it can immediately understand how it works. It uses standard iterator and standard list containers. Not many programmers rely on it to install things, just standard C ++. This is an important step forward. This example uses STL to make our software more standard.
Use the General STL algorithm for_each to process elements in the list.
With STL list and iterator, We need to initialize, compare, and incrementally traverse this container for iterator. The general for_each algorithm of STL can reduce our work.
/*|| How to print a simple STL list MkII*/#include <iostream.h>#include <string>#include <list>#include <algorithm>PrintIt (string& StringToPrint) { cout << StringToPrint << endl;}int main (void) { list<string> FruitAndVegetables; FruitAndVegetables.push_back("carrot"); FruitAndVegetables.push_back("pumpkin"); FruitAndVegetables.push_back("potato"); FruitAndVegetables.push_front("apple"); FruitAndVegetables.push_front("pineapple"); for_each (FruitAndVegetables.begin(), FruitAndVegetables.end(), PrintIt);}
In this program, we use the general STL algorithm for_each () to traverse the range of an iterator, and then call printit () to process each object. We do not need to initialize, compare, and increment iterator. For_each () has done these jobs for us. The operations we execute on objects are well packaged outside of this function, so we don't need to make that loop any more, and our code is clearer.
The for_each algorithm references the concept of the iterator range, which is a range specified by the starting iterator and the end iterator. The START iterator indicates where the operation starts, and the end iterator indicates where the operation ends, but it is not included in this range.
The general algorithm count () of STL is used to count the number of elements in the list.
The general algorithm count () and count_it () of STL are used to count objects in containers. Like for_each (), the count () and count_if () algorithms are also implemented in the iterator range.
Let's count a full score in the list of student test scores. This is an integer list.
/*|| How to count objects in an STL list*/#include <list>#include <algorithm>#int main (void) { list<int> Scores;# Scores.push_back(100); Scores.push_back(80); Scores.push_back(45); Scores.push_back(75); Scores.push_back(99); Scores.push_back(100);# int NumberOf100Scores(0); count (Scores.begin(), Scores.end(), 100, NumberOf100Scores);# cout << "There were " << NumberOf100Scores << " scores of 100" << endl;}
The count () algorithm counts the number of objects equal to a value. In the above example, it checks whether each integer object in the list is 100. Every time the object in the container is equal to 100, it adds 1 to numberof100scores. This is the output of the program:
There were 2 scores of 100
Count the number of elements in the list using the general algorithm count_if () of STL.
Count_if () is a more interesting version of Count. It uses a new STL component, a function object. Count_if () includes the parameters of a function object. A function object is a class with at least one operator () method. Some STL algorithms receive function objects as parameters and call the operator () method of this function object.
The function object is agreed to return true or false when the STL algorithm calls operator. They determine this function based on this. For example, we will make it clearer. Count_if () evaluates more complex than count () by passing a function object to determine whether an object should be counted. In this example, we will count the number of toothbrushes sold. We will submit a four-character sales code and product description sales records.
/*|| Using a function object to help count things*/#include <string>#include <list>#include <algorithm>const string ToothbrushCode("0003");class IsAToothbrush {public: bool operator() ( string& SalesRecord ) { return SalesRecord.substr(0,4)==ToothbrushCode; } };int main (void) { list<string> SalesRecords; SalesRecords.push_back("0001 Soap"); SalesRecords.push_back("0002 Shampoo"); SalesRecords.push_back("0003 Toothbrush"); SalesRecords.push_back("0004 Toothpaste"); SalesRecords.push_back("0003 Toothbrush"); int NumberOfToothbrushes(0); count_if (SalesRecords.begin(), SalesRecords.end(), IsAToothbrush(), NumberOfToothbrushes); cout << "There were " << NumberOfToothbrushes << " toothbrushes sold" << endl;}
This is the output of this program:
There were 2 toothbrushes sold
This program works like this: defines a function object class isatoothbrush, the class object can determine whether the sold is a toothbrush. If this record is a sale record, the function calls operator () to return a true value; otherwise, false is returned.
The count_if () algorithm processes container objects in the range specified by the first and second iterator parameters. It will add the numberoftoothbrushes value to the objects in the container where each isatoothbrush () returns true.
The final result is that the numberoftoothbrushes variable saves the number of records with the product code field "0003", that is, the number of toothbrushes.
Note that the third parameter isatoothbrush () of count_if () is an object temporarily constructed by its constructor. You can pass a temporary object of the isatoothbrush class to the count_if () function. Count_if () will call this function for each object of the container.
A more complex function object using count_if.
We can further study function objects. Suppose we need to pass more information to a function object. We cannot do this by calling operator, because it must be defined as the object type in a list. However, by pointing out a non-default constructor for isatoothbrush, We can initialize it with any information we need. For example, we may need an indefinite code for each toothbrush. We can add this information to the following function object:
/*|| Using a more complex function object*/#include <iostream.h>#include <string>#include <list>#include <algorithm>class IsAToothbrush {public: IsAToothbrush(string& InToothbrushCode) : ToothbrushCode(InToothbrushCode) {} bool operator() (string& SalesRecord) { return SalesRecord.substr(0,4)==ToothbrushCode; } private: string ToothbrushCode; };int main (void) { list<string> SalesRecords; SalesRecords.push_back("0001 Soap"); SalesRecords.push_back("0002 Shampoo"); SalesRecords.push_back("0003 Toothbrush"); SalesRecords.push_back("0004 Toothpaste"); SalesRecords.push_back("0003 Toothbrush"); string VariableToothbrushCode("0003"); int NumberOfToothbrushes(0); count_if (SalesRecords.begin(), SalesRecords.end(), IsAToothbrush(VariableToothbrushCode), NumberOfToothbrushes); cout << "There were " << NumberOfToothbrushes << " toothbrushes matching code " << VariableToothbrushCode << " sold" << endl;}
The program output is:
There were 2 toothbrushes matching code 0003 sold
This example demonstrates how to pass information to a function object. You can define Any constructor you want, and you can compile the constructor in the function object as required.
You can see that the function object actually extends the Basic counting algorithm.
So far, we have learned:
- Define a list
- Add element to list
- How do I know whether list is empty?
- How to Use the for loop to traverse a list
- How to use the general STL algorithm for_each to traverse the list
- List member functions begin () and end () and their meanings
- The concept of iterator range and the last position of a range are not actually handled.
- How to Use General STL algorithms count () and count_if () to count objects in a list
- How to define a function object
I use these examples to demonstrate the General List operations. If you understand these basic principles, you can use STL without any doubt. We suggest you do some exercises. We now use more complex operations to expand our knowledge, including List member functions and general STL algorithms.