20- C++ STL Standard Template Library-8 (C++)

chapter Ten

10.1 Basic concepts

STL (Standard Template Library, Standard Template Library ) is a collective name for a series of software developed by Hewlett-Packard Labs. Now mainly appears in C++, but the technology has existed for a long time before being introduced into C++.

STL is broadly divided into three categories: algorithm (algorithm), container (container) and iterator (iterator). Containers and algorithms can be seamlessly connected through iterators. Almost all the code uses template classes and template functions , which provides better code reuse opportunities than traditional libraries composed of functions and classes. In the C++ standard, the STL is organized into the following 13 header files:

<algorithm>、<deque>、<functional>、<iterator>、<vector>、<list>、<map>、<memory>、<numeric>、<queue>、<set>、<stack> 和<utility>

Benefits of using STL

1) STL is part of C++, so there is no need to install anything, it is built into your compiler.

2) An important feature of STL is the separation of data structures and algorithms. Although a simple concept, this separation does make the STL very general.

For example, in the vector container of STL, you can put elements, variables of basic data types, and addresses of elements;

The sort() function of STL can be used to operate containers such as vector and list.

3) Programmers don't need to think about the specific implementation process of STL, as long as they can use STL proficiently, it will be OK. This way they can focus on other aspects of program development.

4) STL has the advantages of high reusability, high performance, high portability, and cross-platform.

  • High reusability : Almost all codes in STL are implemented in the form of template classes and template functions , which provides better code reuse opportunities than traditional libraries composed of functions and classes.

  • High performance : For example, map can efficiently find specified records from 100,000 records, because map isimplemented using a variant of red-black tree . (Red-black tree is a kind of horizontal binary tree)

  • High portability : For example, a module written in STL on project A can be directly transplanted to project B.

  • Cross-platform : For example, code written in Visual Studio of Windows can be directly compiled on XCode of Mac OS.

10.2 Containers

10.2.1 Classification of containers

1. Sequence containers

  • Each element has a fixed position -- determined by when and where it is inserted, independent of the element's value.

  • vector、deque、list、stack、queue

2. Associated containers

  • Element position depends on a specific sorting criterion, independent of insertion order

  • set、multiset、map、multimap

10.2.2 vector containers

1. Introduction to vector container

  • Vector is a container that manages elements in a dynamic array .

  • Vector can randomly access elements ( supports direct access to index values, using the [] operator or at() method , which will be discussed in detail later).

  • Adding or removing elements from the end of a vector is very fast. But it is time-consuming to insert elements or remove elements in the middle or head

2. The default construction of the vector object

Vector is implemented by template class , and the default construction form of vector object

vector<T> vecT;
vector<int> vecInt;  //存放int的vector的容器
vector<float> vecFloat;  //存放float的容器
vector<string> vecString;  //存放string的容器

class CA{};
vector<CA*> vecpCA;  //存放CA对象的指针的容器
vector<CA> vecCA;   // 存放CA对象的容器

3. The parameterized construction of the vector object

theoretical knowledge

  • vector(beg, end); // The constructor copies the elements in the interval [beg, end) to itself . Note that this interval is a left-closed right-open interval.

  • vector(n,elem); // The constructor copies n elems to itself .

  • vector(const vector &vec); // copy constructor

int iArray[] = {0, 1, 2, 3, 4};
vector<int> vecIntA(iArray, iArray+5);
// 用构造函数初始化容器
vector<int> vecIntB(vecIntA.begin(), vecIntA.end());
vector<int> vecIntE(vecIntA.begin(), vecIntA.begin() + 3);
vector<int> vecIntC(3, 9);
vector<int> vecIntD(vecIntA);

4. Assignment of vector

theoretical knowledge

  • vector.assign(beg,end) ; //Assign the data copy in [beg, end) range to itself. Note that this interval is a left-closed right-open interval.

  • vector.assign(n,elem) ; //Assign n copies of elem to itself.

  • vector& operator=(const vector &vec) ; // overloaded equal sign operator

  • vector.swap(vec) ; // Swap vec with its own elements.

int main() {
    vector<int> vecIntA, vecIntB, vecIntC;
    int iArray[] = {0, 1, 2, 3, 4};
    vecIntA.assign(iArray, iArray + 5);
    vecIntB.assign(vecIntA.begin(), vecIntA.end());
    vecIntC.assign(3, 9);
    vector<int> vecIntD;
    vecIntD = vecIntA;
    vecIntA.swap(vecIntD);
    return 0;
}

5. The size of the vector

theoretical knowledge

  • vector.size();    // returns the number of elements in the container

  • vector.empty();    // Determine whether the container is empty

  • vector.resize(num);    // Respecify the length of the container as num, if the container becomes longer, fill the new position with the default value. If the container gets shorter, elements at the end that exceed the length of the container are removed.

  • vector.resize (num, elem); //Respecify the length of the container as num, if the container becomes longer, fill the new position with the value of elem . If the container gets shorter, elements at the end that exceed the length of the container are removed.

> For example, vecInt is the container declared by vector , which now contains 1,2,3 elements.

> int iSize = vecInt.size();   //iSize == 3;

> bool bEmpty = vecInt.empty();   // bEmpty == false;

> Execute vecInt.resize(5) ; //At this time, it contains 1,2,3,0,0 elements.

> Execute vecInt.resize(8,3) again ; //At this time, it contains 1,2,3,0,0,3,3,3 elements.

> Execute vecInt.resize(2); //At this time, it only contains 1 and 2 elements.

6. Add and remove operations at the end of the vector

  • vector vecInt;

  • vecInt .push_back(1) ; // Add an element at the end of the container

  • vecInt.push_back(3);

  • vecInt.push_back(5);

  • vecInt.push_back(7);

  • vecInt.push_back(9);

  • vecInt.pop_back();

  • vecInt.pop_back();

7. Vector data access

Theoretical knowledge:

  • vec.at(idx) ; // Return the data pointed by the index idx , if idx is out of bounds, throw an out_of_range exception.

  • vec[idx] ; // Return the data pointed to by the index idx , when it is out of bounds, the operation will directly report an error

int main() {
    vector<int> vecInt;
    vecInt.at(2) == vecInt[2];
    vecInt.at(2) = 8;
    int iF = vecInt.front();
    int iB = vecInt.back();
    vecInt.front() = 11;
    vecInt.back() = 19;
    
    return 0;
}

8. Vector insertion

theoretical knowledge

  • vector.insert(pos, elem);     //Insert a copy of the elem element at position pos, and return the position of the new data.

  • vector.insert(pos, n, elem);     //Insert n elem data at position pos, no return value.

  • vector.insert(pos, beg, end);     //Insert the data in [beg, end) range at position pos, no return value

Simple case:

int main() {
    vector<int> vecA;
    vector<int> vecB;
    
    vecA.push_back(1);
    vecA.push_back(3);
    vecA.push_back(5);
    vecA.push_back(7);
    vecA.push_back(9);
    
    vecB.push_back(2);
    vecB.push_back(4);
    vecB.push_back(6);
    vecB.push_back(8);
    
    vecA.insert(vecA.begin(), 11);
    vecA.insert(vecA.begin() + 1, 2, 33);
    vecA.insert(vecA.begin(), vecB.begin(), vecB.end());
    return 0;
}

9. Deletion of vector

theoretical knowledge

  • vector.clear();     // remove all data from the container

  • vec.erase(beg,end);    // Delete the data in the interval [beg,end), and return the position of the next data.

  • vec.erase(pos);     // Delete the data at position pos and return the position of the next data.

Simple case:

vector<int> vecInt;
vecInt.erase(vecInt.begin(), vecInt.begin() + 3);
vecInt.clear();

10. Iterators

1) The basic concept of iterator

  • What are iterators:

    • An iterator is a data type that examines and iterates over the elements in a container .

  • The role of iterators:

    • Iterators provide access to the objects in a container and define the range of objects in the container.

  • Why do you need iterators:

    • STL provides a variety of containers, and the implementation principles of each container are different. If there is no iterator, we need to remember the access method of each object in the container. Obviously, this will become very troublesome.

    • Many containers provided by STL implement an iterator to access objects in the container. Although the implementation of the iterator in each container is different, the operation method is the same for the user, that is, through iteration The container unifies access to all containers. For example: to access the next element of the current element, we can access it through iterator increment.

  • Iterators were developed for programming efficiency.

  • The nature of iterators:

    • An iterator is an embedded class specially implemented in a container class to access data in a container (a class within a class)

In order to unify the operation of the iterator in each container, typedef will be used in the container class to alias the iterator class, and the alias is: iterator

The access method of the iterator class to the elements in the container: pointer

The specific implementation of the iterator class: In order to hide the specific implementation of the iterator in each container, and to unify the user's access method for the iterator in each container, the user can use the iterator as a pointer to access the elements in the container. But because iterators are not pointers, we need to overload operators such as *, ->, pre ++/--, post ++/-- in the iterator class.

template <typename T>
class list_iterator
{
public:
    T &operator() const {}
    node<T>*operator->() const {}
    list_iterator &operator++() {}
    list_iterator operator++(int) {}
    bool operator==(const list_iterator &t) const {}
    bool operator!=(const list_iterator &t) const {}
};

2) The iterator of the vector container

Each container type defines its own iterator type, such as vector:

vector<int>::iterator iter;

3) Member functions in the vector container iterator class

The iterator of the vector container is a " random access iterator ": the iterator can move multiple positions at a time

4) begin and end operations

Each container defines a set of functions named begin and end that return iterators. The element returned by begin points to the first element, if any, in the container.

vector<int>::iterator iter=v.begin(); 

The iterator returned by end points to the next element of the last element. If v is empty, begin and end return the same.

  • ++iter;    //Make iterator self-increment pointing to the next element

  • == and != operators to compare two iterators, if the two iterators point to the same element, they are equal, otherwise do not want to wait.

Example of using iterators:

for(vector<int>::iterator iter = v.begin(); iter!=v.end(); iter++)
    *iter = 0;

5) Arithmetic operations on iterators

  • iter+n;   //The iterator iter plus n refers to the position after adding n elements before the position i of the current iterator (such as the position of the first element of the vector).

  • iter-n;   //Iterator iter minus n, refers to the position minus n elements after the position of the current iterator

5) Iterator invalidation

  • Inserting an element invalidates the iterator

Let's look at such a piece of code first:

int main() {
    vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);

    vector<int>::iterator it1 = v.begin() + 3;
    v.insert(it1, 8);
    cout << *it1 << endl;
    return 0;
}

Running the above code, we will find that the output result is not 8, and it may even cause the program to crash. Why is this?

Because during insert, the vector may need to be expanded, and the essence of expansion is to create a new space, and then migrate the data there. And we know that the inside of the iterator accesses the elements in the container through pointers, and after insertion, if the vector expands, the original data will be released, and the iterator pointing to the original data will become a wild pointer, so the iterator failed.

The solution is very simple. The insert function provides a return value, which is an iterator pointing to the inserted val. We just need to save the returned iterator and use this new iterator.

int main() {
    vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);

    vector<int>::iterator it1 = v.begin() + 3;
    it1 = v.insert(it1, 8);
    cout << *it1 << endl;
    return 0;
}
  • Deleting an element invalidates the iterator

Let's look at this piece of code first:

int main() {
    vector<int> cont = {1, 2 ,3 ,4 ,5, 6, 5};
    for (iter = cont.begin(); iter != cont.end(); iter++)
    {
        if (*iter == 3)
            cont.erase(iter);
    }
    return 0;
}

For sequential containers (such as vector, deque), the sequential container is an array container, and deleting the current iterator will invalidate the iterators of all subsequent elements. This is because vetor and deque use continuously allocated memory, and deleting an element causes all subsequent elements to move forward by one position. So you can't use the erase(iter++) method. Fortunately, the erase method can return the next valid iterator.

Solution:

int main() {
    vector<int> cont = {1, 2 ,3 ,4 ,5, 6, 5};
    vector<int>::iterator iter; // 声明迭代器的类型
    for (iter = cont.begin(); iter != cont.end(); iter++)
    {
        if (*iter == 3)
            iter = cont.erase(iter);
        else
            iter++;
    }
    return 0;
}

10.2.3 deque container

Introduction to deques:

  • Deque is the abbreviation of "double-ended queue". Like vector, it is an STL container. Deque is a double-ended array , while vector is single-ended.

  • Deque is very similar to vector in interface, and can be directly replaced in many operations.

  • Deque can randomly access elements (support index value direct access, use [] operator or at() method,

  • Both adding and removing elements from the head and tail of the deque are very fast. But inserting elements or removing elements in the middle is more time-consuming.

Deque is almost the same as vector in operation, and deque has two more functions:

  • deque.push_front(elem) ; //Insert a data at the head of the container

  • deque.pop_front();     //Delete the first data in the container

10.2.4 list container

1. Introduction to list

  • list is a doubly linked list container that can efficiently insert and delete elements.

  • The list cannot randomly access elements , so the at.(pos) function and the [] operator are not supported. It++(ok) it+5(err)

2. The default structure of the list object

The list is implemented by a template class, and the default construction form of the object is: list <T> lst such as:

list<int> lstInt;            //定义一个存放int的list容器。
list<float> lstFloat;        //定义一个存放float的list容器。
list<string> lstString;      //定义一个存放string的list容器。

3. Add and remove operations at the head and tail of the list

  • list.push_back(elem);    // Add an element to the end of the container

  • list.pop_back();    // delete the last element in the container

  • list.push_front(elem);    // insert an element at the beginning of the container

  • list.pop_front();     // remove the first element from the beginning of the container

list<int> lstInt;
lstInt.push_back(1);
lstInt.push_back(3);
lstInt.push_back(5);
lstInt.push_back(7);
lstInt.push_back(9);
lstInt.pop_front();
lstInt.pop_front();
lstInt.push_front(11);
lstInt.push_front(13);
lstInt.pop_back();
lstInt.pop_back();

4. List data access

  • list.front();     // Returns the first element.

  • list.back();    // Return the last element.

list<int> lstInt;
lstInt.push_back(1);
lstInt.push_back(3);
lstInt.push_back(5);
lstInt.push_back(7);
lstInt.push_back(9);
 
int iFront = lstInt.front(); //1
int iBack = lstInt.back(); //9
lstInt.front() = 11; //11
lstInt.back() = 19; //19

5. list and iterator

Iterators for list containers are "bidirectional iterators": bidirectional iterators read and write to the container in both directions. In addition to providing all operations of forward iterators, bidirectional iterators also provide pre- and post-decrement operations

  • list.begin();     // Returns an iterator to the first element in the container.

  • list.end();       // Returns an iterator past the last element in the container.

  • list.rbegin();   // Returns an iterator to the first last element in the container.

  • list.rend();     // Returns the iterator behind the last last element in the container.

list<int> lstInt;
lstInt.push_back(1);
lstInt.push_back(3);
lstInt.push_back(5);
lstInt.push_back(7);
lstInt.push_back(9);
 
for (list<int>::iterator it=lstInt.begin(); it!=lstInt.end(); ++it)
{
    cout << *it;
    cout << " ";
}
 
for (list<int>::reverse_iterator rit=lstInt.rbegin(); rit!=lstInt.rend(); ++rit)
{
    cout << *rit;
    cout << " ";
}

6. The parameterized structure of the list object

  • list(n,elem);     // The constructor copies n elems to itself.

  • list(beg,end);   // The constructor copies the elements in the interval [beg,end] to itself

  • list(const list &lst);   // copy constructor.

list<int> lstIntA;
lstIntA.push_back(1);
lstIntA.push_back(3);
lstIntA.push_back(5);
lstIntA.push_back(7);
lstIntA.push_back(9);
 
list<int> lstIntB(lstIntA.begin(),lstIntA.end()); //1 3 5 7 9
list<int> lstIntC(5,8); //8 8 8 8 8
list<int> lstIntD(lstIntA); //1 3 5 7 9

7. List assignment

  • list.assign(beg,end);     // Assign the copy of the data in the [beg, end) range to itself. Note that this interval is a left-closed right-open interval.

  • list.assign(n,elem);      // Assign n elem copies to itself.

  • list& operator=(const list &lst);    // overloaded equals operator

  • list.swap(lst);     // Swap lst with its own elements.

list<int> lstIntA,lstIntB,lstIntC,lstIntD;
lstIntA.push_back(1);
lstIntA.push_back(3);
lstIntA.push_back(5);
lstIntA.push_back(7);
lstIntA.push_back(9);
 
lstIntB.assign(lstIntA.begin(),lstIntA.end()); //1 3 5 7 9
lstIntC.assign(5,8); //8 8 8 8 8
lstIntD = lstIntA; //1 3 5 7 9
lstIntC.swap(lstIntD); //互换

8. The size of the list

  • list.size();        // returns the number of elements in the container

  • list.empty();    // Determine whether the container is empty

  • list.resize(num);   // Re-specify the length of the container as num, if the container becomes longer, fill the new position with the default value. If the container gets shorter, elements at the end that exceed the length of the container are removed.

  • list.resize(num, elem);   // Re-designate the length of the container as num. If the container becomes longer, fill the new position with the value of elem. If the container gets shorter, elements at the end that exceed the length of the container are removed.

list<int> lstIntA;
lstIntA.push_back(1);
lstIntA.push_back(3);
lstIntA.push_back(5);
 
if (!lstIntA.empty())
{
    int iSize = lstIntA.size(); //3
    lstIntA.resize(5); //1 3 5 0 0
    lstIntA.resize(7,1); //1 3 5 0 0 1 1
    lstIntA.resize(2); //1 3
}

9. Insertion of list

  • list.insert(pos,elem);     // Insert a copy of the elem element at position pos, and return the position of the new data.

  • list.insert(pos,n,elem);     // Insert n elem data at position pos, no return value.

  • list.insert(pos,beg,end);    // Insert the data in [beg,end) range at position pos, no return value.

list<int> lstA;
list<int> lstB;
 
lstA.push_back(1);
lstA.push_back(3);
lstA.push_back(5);
lstA.push_back(7);
lstA.push_back(9);
 
lstB.push_back(2);
lstB.push_back(4);
lstB.push_back(6);
lstB.push_back(8);
 
lstA.insert(lstA.begin(), 11); //{11, 1, 3, 5, 7, 9}
lstA.insert(++lstA.begin(),2,33); //{11,33,33,1,3,5,7,9}
lstA.insert(lstA.begin() , lstB.begin() , lstB.end() );

10. Delete list

  • list.clear();     // remove all data from the container

  • list.erase(beg,end);    // Delete the data in the range [beg,end), and return the position of the next data.

  • list.erase(pos);    // Delete the data at position pos and return the position of the next data.

  • lst.remove(elem);    // Delete all elements in the container that match the value of elem.

list<int>::iterator itBegin=lstInt.begin();
++itBegin;
list<int>::iterator itEnd=lstInt.begin();
++itEnd;
++itEnd;
++itEnd;
lstInt.erase(itBegin,itEnd);
//此时容器lstInt包含按顺序的1,6,9三个元素。

lstA.push_back(3);
lstA.push_back(3);
lstA.remove(3); //将list中所有的3删除
lstA.clear();//容器为空

11. The reverse order of the list

  • lst.reverse();    // Reverse the linked list, for example, lst contains 1, 3, 5 elements, after running this method, lst contains 5, 3, 1 elements.

list<int> lstA;
lstA.push_back(1);
lstA.push_back(3);
lstA.push_back(5);
lstA.push_back(7);
lstA.push_back(9);
 
lstA.reverse(); //9 7 5 3 1

12. The list iterator fails

  • Deleting a node invalidates the iterator

for(list<int>::iterator it=lstInt.being(); it!=lstInt.end(); )    //小括号里不需写  ++it
{
   if(*it == 3)
   {
       lstInt.erase(it);  //以迭代器为参数,删除元素3,并把数据删除后的下一个元素位置返回给迭代器。
   }
}

10.2.5 stack container

1. Introduction to Stack

  • stack is a stack container , which is a "first in, last out" container.

  • The stack simply decorates the deque container to become another kind of container.

2. The default structure of the stack object

> The stack is implemented using a template class, and the default construction form of the stack object:

stack <int> stkInt;            //一个存放int的stack容器。
stack <float> stkFloat;        //一个存放float的stack容器。
stack <string> stkString;      //一个存放string的stack容器。

3. The push() and pop() methods of the stack

stack.push(elem);   //往栈头添加元素
stack.pop();   //从栈头移除第一个元素
 
stack<int> stkInt;  
stkInt.push(1);stkInt.push(3);stkInt.pop();   
stkInt.push(5);stkInt.push(7);  
stkInt.push(9);stkInt.pop();   
stkInt.pop();  
//此时stkInt存放的元素是1,5  

4. Copy construction and assignment of stack objects

  • stack(const stack &stk);   // copy constructor

  • stack& operator=(const stack &stk);    // overloaded equals operator

stack<int> stkIntA;
stkIntA.push(1);
stkIntA.push(3);
stkIntA.push(5);
stkIntA.push(7);
stkIntA.push(9);
 
stack<int> stkIntB(stkIntA); //拷贝构造
stack<int> stkIntC;
stkIntC = stkIntA; //赋值

5. Stack data access

stack.top();   //返回最后一个压入栈元素
stack<int> stkIntA;
stkIntA.push(1);
stkIntA.push(3);
stkIntA.push(5);
stkIntA.push(7);
stkIntA.push(9);
 
int iTop = stkIntA.top(); //9
stkIntA.top() = 19; //19

6. The size of the stack

  • stack.empty();    // determine whether the stack is empty

  • stack.size();        // returns the size of the stack

stack<int> stkIntA;
stkIntA.push(1);
stkIntA.push(3);
stkIntA.push(5);
stkIntA.push(7);
stkIntA.push(9);
 
if (!stkIntA.empty())
{
    int iSize = stkIntA.size(); //5
}

10.2.6 queue container

1. Introduction to Queue

  • queue is a queue container, which is a "first in, first out" container .

2. The default construction of the queue object

The queue is implemented by a template class, and the default construction form of the queue object is: queue <T> q; such as:

queue<int> queInt;            //一个存放int的queue容器。
queue<float> queFloat;        //一个存放float的queue容器。
queue<string> queString;      //一个存放string的queue容器。

3. The push() and pop() methods of the queue

  • queue.push(elem);   // Add elements to the end of the queue

  • queue.pop();    // remove the first element from the head of the queue

queue<int> queInt;
queInt.push(1);queInt.push(3);
queInt.push(5);queInt.push(7);
queInt.push(9);queInt.pop();
queInt.pop();

4. Copy construction and assignment of queue objects

  • queue(const queue &que);     // copy constructor

  • queue& operator=(const queue &que);     // overloaded equals operator

queue<int> queIntA;
queIntA.push(1);
queIntA.push(3);
queIntA.push(5);
queIntA.push(7);
queIntA.push(9);
 
queue<int> queIntB(queIntA); //拷贝构造
queue<int> queIntC;
queIntC = queIntA; //赋值

5. Queue data access

  • queue.back();   // return the last element

  • queue.front();   // return the first element

queue<int> queIntA;
queIntA.push(1);
queIntA.push(3);
queIntA.push(5);
queIntA.push(7);
queIntA.push(9);
 
int iFront = queIntA.front(); //1
int iBack = queIntA.back(); //9
 
queIntA.front() = 11; //11
queIntA.back() = 19; //19

6. The size of the queue

  • queue.empty();    // Determine whether the queue is empty

  • queue.size();      // returns the size of the queue

queue<int> queIntA; 
queIntA.push(1);   
queIntA.push(3);  
queIntA.push(5);
queIntA.push(7);
queIntA.push(9);
 
if (!queIntA.empty())
{
    int iSize = queIntA.size(); //5
}

10.2.7 Set and multiset containers

1. Introduction to set/multiset

  • set is a collection container , the elements contained in it are unique , and the elements in the collection are arranged in a certain order . The element insertion process is inserted according to the sorting rules, so the insertion position cannot be specified.

  • The set is implemented using the data structure of the red-black tree variant , which belongs to the balanced binary tree. Faster than vector for insertion and deletion.

  • set cannot directly access elements. (The at.(pos) and [] operators cannot be used).

  • The difference between multiset and set: set supports unique key values, and each element value can only appear once; while the same value can appear multiple times in multiset .

  • It is not possible to directly modify the element values ​​in the set or multiset container, because this type of container is automatically sorted. If you want to modify the value of an element, you must first delete the original element, and then insert a new element.

2. Default construction of set/multiset objects

set<int> setInt;            //一个存放int的set容器。
set<float> setFloat;     //一个存放float的set容器。
set<string> setString;     //一个存放string的set容器。
multiset<int> mulsetInt;            //一个存放int的multi set容器。
multi set<float> multisetFloat;     //一个存放float的multi set容器。
multi set<string> multisetString;     //一个存放string的multi set容器。

3. Copy construction and assignment of set objects

  • set(const set &st);   // copy constructor

  • set& operator=(const set &st);     // overloaded equals operator

  • set.swap(st);    // swap two collection containers

set<int> setIntA;
setIntA.insert(3);
setIntA.insert(1);
setIntA.insert(7);
setIntA.insert(5);
setIntA.insert(9);
 
set<int> setIntB(setIntA);  //1 3 5 7 9
set<int> setIntC;
setIntC = setIntA; //1 3 5 7 9
 
setIntC.insert(6);
setIntC.swap(setIntA);   //交换

4. The size of the set

  • set.size();       // returns the number of elements in the container

  • set.empty();   // Determine whether the container is empty

set<int> setIntA;
setIntA.insert(3);
setIntA.insert(1);
setIntA.insert(7);
setIntA.insert(5);
setIntA.insert(9);
 
if (!setIntA.empty())
{
    int iSize = setIntA.size(); //5
}

5. Set insertion and iterator

  • set.insert(elem);     // Insert an element into the container.

  • set.begin();     // Returns an iterator to the first data in the container.

  • set.end();     // Returns an iterator after the last data in the container.

  • set.rbegin();    // Returns an iterator to the first last element in the container.

  • set.rend();      // Returns the iterator behind the last last element in the container.

set<int> setInt;
setInt.insert(3); 
setInt.insert(1);
setInt.insert(5);
setInt.insert(2);
for(set<int>::iterator it=setInt.begin(); it!=setInt.end(); ++it)
{
      int iItem = *it;
      cout << iItem;    //或直接使用cout << *it
}

5. Deletion of set

  • set.clear();    // clear all elements

  • set.erase(pos);     // Delete the element pointed by the pos iterator and return the iterator of the next element.

  • set.erase(beg,end);     // Delete all elements in the interval [beg,end), and return the iterator of the next element.

  • set.erase(elem);          // Delete the element whose value is elem in the container.

//删除区间内的元素
//setInt是用set<int>声明的容器,现已包含按顺序的1,3,5,6,9,11元素。
set<int>::iterator itBegin=setInt.begin();
++itBegin;
set<int>::iterator itEnd=setInt.begin();
++itEnd;
++itEnd;
++itEnd;
setInt.erase(itBegin,itEnd);
//此时容器setInt包含按顺序的1,6,9,11四个元素。
 
//删除容器中第一个元素
setInt.erase(setInt.begin()); //6,9,11
 
//删除容器中值为9的元素
set.erase(9);    
 
//删除setInt的所有元素
setInt.clear(); //容器为空

6. Sorting the elements of the set collection

set<int,greater<int>> setIntB;   
setIntB.insert(3);
setIntB.insert(1);
setIntB.insert(5);
setIntB.insert(2);
// 此时容器setIntB就包含了按顺序的5,3,2,1元素

Usage of function object functor

  • Although function pointers are widely used to implement function callbacks, C++ also provides an important way to implement callback functions, that is, function objects.

  • functor, translated into function object , pseudo function, operator, is an ordinary class object that overloads the "()" operator. Syntactically, it behaves like a normal function.

  • greater<> and less<> are function objects.

The simple implementation principle of greater is listed below:

class greater
{
    bool operator() (const int& iLeft, const int& iRight)
    {
           return (iLeft>iRight);    //如果是实现less<int>的话,这边是写return (iLeft<iRight);
    }
}

The container is to call the operator() method of the function object to compare the size of the two values .

Thinking: Students include student number and name attributes, now it is required to insert several student objects into the set container arbitrarily, so that the students in the container are sorted in ascending order of student number.

//学生类
class CStudent
{
    public:
    CStudent(int iID, string strName)
    {
          m_iID = iID;
          m_strName = strName;
     }
     int m_iID; //学号
     string m_strName; //姓名
}
//本类不写拷贝构造函数。但大家仍要有考虑拷贝构造函数的习惯。
//函数对象
class StuFunctor
{
    bool operator()  (const CStudent &stu1, const CStudent &stu2)
    {
        return (stu1.m_iID<stu2.m_iID);
    }
}
 
//main函数
int main()
{
   set<CStudent, StuFunctor> setStu;
   setStu.insert(CStudent(3,"小张"));
   setStu.insert(CStudent(1,"小李"));
   setStu.insert(CStudent(5,"小王"));
   setStu.insert(CStudent(2,"小刘"));
   //此时容器setStu包含了四个学生对象,分别是按姓名顺序的“小李”,“小刘”,“小张”,“小王” 
}

7. Set search

  • set.find(elem);     // Find the elem element and return an iterator pointing to the elem element.

  • set.count(elem);    // Return the number of elements whose value is elem in the container. For set, either 0 or 1. For multiset, the value may be greater than 1.

  • set.lower_bound(elem);    // returns an iterator to the first >=elem element.

  • set.upper_bound(elem);   // Returns an iterator to the first >elem element.

set<int> setInt;
setInt.insert(3);
setInt.insert(1);
setInt.insert(7);
setInt.insert(5);
setInt.insert(9);
 
set<int>::iterator itA = setInt.find(5);
int iA = *itA; //iA == 5
int iCount = setInt.count(5); //iCount == 1
 
set<int>::iterator itB = setInt.lower_bound(5);
set<int>::iterator itC = setInt.upper_bound(5);
int iB = *itB; //iB == 5
int iC = *itC; //iC == 7

set.equal_range(elem);     // Returns two iterators of upper and lower bounds equal to elem in the container. The upper limit is a closed interval, and the lower limit is an open interval, such as [beg,end).

  • The function returns two iterators, and these two iterators are encapsulated in a pair.

pair< set<int>::iterator, set<int>::iterator > pairIt = setInt.equal_range(5);  
//pair是什么?

//pair译为对组,可以将两个值视为一个单元。
//pair<T1,T2>存放的两个值的类型,可以不一样,如T1为int,T2为float。T1,T2也可以是自定义类型。
//pair.first是pair里面的第一个值,是T1类型。
//pair.second是pair里面的第二个值,是T2类型。

10.2.8 map and multimap containers

1. Introduction to map/multimap

  • A map is a standard associative container , and a map is a sequence of key-value pairs, that is, (key, value) pairs. It provides fast key-based retrieval capabilities.

  • The key value in the map is unique . The elements in a collection are arranged in a certain order. The element insertion process is inserted according to the sorting rules, so the insertion position cannot be specified.

  • The specific implementation of map adopts the data structure of a balanced binary tree variant of the red-black tree. Faster than vector for insertion and deletion.

  • map can directly access the value corresponding to the key, and supports the [] operator, such as map[key]=value (change the value corresponding to the key key to value)

  • The difference between multimap and map: map supports unique key values, and each key can only appear once; in multimap, the same key can appear multiple times . multimap does not support the [] operator.

2. Default construction of map/multimap objects

//map/multimap采用模板类实现,对象的默认构造形式:
map<T1,T2> mapTT;
multimap<T1,T2>  multimapTT;  
//如:
map<int, char> mapA;
map<string,float> mapB;
//其中T1,T2还可以用各种指针类型或自定义类型

3. Map insertion and iterators

map.insert(...);    //往容器插入元素,返回pair

There are three ways to insert elements in the map:

hypothetical map mapStu;

1. Insert objects by pair

mapStu.insert(pair(3,"小张") );

2. Insert objects by value_type

mapStu.insert(  map<int,string>::value_type(1,"小李"));

3. Insert values ​​​​through the array

mapStu[3] = “小刘";

mapStu[5] = “小王";

The first two methods use the insert() method , which returns a pair

The third method is very intuitive, but there is a performance problem. When inserting 3, first search for the item whose primary key is 3 in mapStu, if not found, insert a key-value pair with key 3 and value "Xiao Liu" into the map. If it is found that the key 3 already exists, modify the value corresponding to this key to "Xiao Liu".

modify if the key exists, insert if not

string strName = mapStu[2];   // fetch or insert

Only when the key 2 exists in mapStu is the correct fetch operation, otherwise an instance will be automatically inserted, the key is 2, and the value is the initialization value.

map<int, string> mapA;
pair<map<int,string>::iterator, bool> pairResult = 
                        mapA.insert(pair<int,string>(3,"小张")); //插入方式一
 
int iFirstFirst = (pairResult.first)->first; //iFirst == 3;
string strFirstSecond = (pairResult.first)->second; //strFirstSecond为"小张"
bool bSecond = pairResult.second; //bSecond == true;
mapA.insert(map<int,string>::value_type(1,"小李")); //插入方式二
mapA[3] = "小刘"; //修改value
mapA[5] = "小王"; //插入方式三
 
string str1 = mapA[2]; //执行插入 string() 操作,返回的str1的字符串内容为空。
string str2 = mapA[3]; //取得value,str2为"小刘"

Use iterators to traverse:

for (map<int,string>::iterator it=mapA.begin(); it!=mapA.end(); ++it)
{
    pair<int, string> pr = *it;
    int iKey = pr.first;
    string strValue = pr.second;
}

4. The value corresponding to the map container or key

Method 1: Use []

Method 2: Use the find() function : return the corresponding iterator successfully, and return the return value of end() if it fails

map<int, string>::iterator it = mapS.find(3);

Method 3: Use the at() function, if the key-value pair does not exist, an "out_of_range exception" will be thrown

5. Copy construction and assignment of map objects

  • map(const map &mp);    // copy constructor

  • map& operator=(const map &mp);   // overloaded equals operator

  • map.swap(mp);    // swap two collection containers

map<int, string> mapA;
mapA.insert(pair<int,string>(3,"小张"));
mapA.insert(pair<int,string>(1,"小杨"));
mapA.insert(pair<int,string>(7,"小赵"));
mapA.insert(pair<int,string>(5,"小王"));
 
map<int ,string> mapB(mapA); //拷贝构造
map<int, string> mapC;
mapC = mapA; //赋值
 
mapC[3] = "老张";
mapC.swap(mapA); //交换

6. The size of the map

  • map.size();   // returns the number of elements in the container

  • map.empty();  // Determine whether the container is empty

map<int, string> mapA;
mapA.insert(pair<int,string>(3,"小张"));
mapA.insert(pair<int,string>(1,"小杨"));
mapA.insert(pair<int,string>(7,"小赵"));
mapA.insert(pair<int,string>(5,"小王"));
 
if (mapA.empty())
{
    int iSize = mapA.size(); //iSize == 4
}

7. Deletion of map

  • map.clear();     // delete all elements

  • map.erase(pos);    // Delete the element pointed by the pos iterator and return the iterator of the next element.

  • map.erase(beg,end);     // Delete all elements in the interval [beg,end), and return the iterator of the next element.

  • map.erase(keyElem);    // Delete the pair whose key is keyElem in the container.

map<int, string> mapA;
mapA.insert(pair<int,string>(3,"小张"));
mapA.insert(pair<int,string>(1,"小杨"));
mapA.insert(pair<int,string>(7,"小赵"));
mapA.insert(pair<int,string>(5,"小王"));

Delete elements in a range:

map<int,string>::iterator itBegin=mapA.begin();
++ itBegin;
++ itBegin;
map<int,string>::iterator itEnd=mapA.end();
mapA.erase(itBegin,itEnd); //此时容器mapA包含按顺序的{1,"小杨"}{3,"小张"}两个元素。
 
mapA.insert(pair<int,string>(7,"小赵"));
mapA.insert(pair<int,string>(5,"小王"));

Remove the specified elements in the container:

mapA.erase(5);    

Delete the element at the specified position in the container:

mapA.erase(mapA.begin());

8. Map search

  • map.find(key);   // Find whether the key key exists, if it exists, return the iterator of the element of the key; if not, return map.end();

  • map.count(keyElem);    // Return the number of pairs whose key is keyElem in the container .

  • map.lower_bound(elem);   // Returns an iterator to the first >=elem element .

  • map.upper_bound(elem);   // Returns an iterator to the first >elem element .

  • map.equal_range(elem);     // Returns two iterators of upper and lower bounds equal to elem in the container . The upper limit is a closed interval, and the lower limit is an open interval, such as [beg,end).

map<int,string>::iterator it=mapStu.find(3);
if(it == mapStu.end())
{
//没找到
}
else
{
    //找到了
    pair<int, string> pairStu = *it;
    int iID = pairStu.first; //或   int  iID = it->first;
    string strName = pairStu.second; //或   string strName = it->second;
}

10.2.9 Summary

Feature comparison:

Underlying implementation:  

 

 

 

Guess you like

Origin blog.csdn.net/March_A/article/details/132051467