c++ generic programming/enhanced programming/c++ introduction

1 function template

The template is to create a general mold, which greatly improves the reusability

Features of the template:

  • The template cannot be used directly, it is just a framework
  • The generality of the template is not a panacea

1.1 Function templates

  • C++ Another programming idea is calledgeneric programming, the main technology used is the template
  • C++ provides two template mechanisms: function templates and class templates

The role of function templates: to create a general function, the return value type and formal parameter type of the function can not be specified, and a virtual type is used to represent

template<typename T> 函数声明或定义

template — declares a template to create

typename — the symbol behind it is a data type, which can be replaced by class

T — general purpose data type, the name can be replaced, usually in uppercase letters

The template is to let the compiler automatically generate a large number of functions, and the process of generating functions from the template is called the instantiation of the template.

  • When we exchange the values ​​of two variables, we need to write two exchange functions because their types are different, but the two functions only have different types of variables, and all other aspects are consistent, so the concept of templates can be used to simplify the code
  • The purpose of the template is to parameterize the type in order to improve reusability; the compiler automatically generates the corresponding type in the formal parameter and structure according to the actual parameter type of the call
#include <iostream>
using namespace std;

// 交换整型变量
void swapInt(int& a, int& b)
{
    
    
	int t = a;
	a = b;
	b = t;
}

// 交换 double 型变量
void swapInt(double& a, double& b)
{
    
    
	double t = a;
	a = b;
	b = t;
}

// 利用模板交换变量
template<typename T> void mySwap(T& a, T&b)
{
    
    
	T t = a;
	a = b;
	b = t;
}

void test01()
{
    
    
	int a = 10, b = 20;
	double c = 1.1, d = 2.2;

	// 两种使用模板的方式
	// 1.自动类型推导
	mySwap(a, b);  // 使用自动类型推导时候,要确保a和b的类型是一致的,否则编译错误

	cout << "a = " << a << endl;
	cout << "b = " << b << endl;

	// 2.显示指定类型
	mySwap<double>(c, d);  // 但是这里<>里的类型必须与 c d 类型一直,否则编译不通过
	cout << "c = " << c << endl;
	cout << "d = " << d << endl;
}
  • The template must determine the data type of T before it can be used
template<class T> void func()
{
    
    
	cout << "func 调用" << endl;
}

void test01()
{
    
    
	//func();  // 错误,模板不能独立使用,必须确定出 T 的类型
	func<int>();  // 利用显示指定类型的方式,给 T 一个类型,才可以使用该模板
}
  • Case: Use function templates to sort data of different data types from large to small
#include <iostream>

using namespace std;


template<typename T> void mySwap(T& a, T& b)
{
    
    
	T t = a;
	a = b;
	b = t;
}

template<typename T> void mySort(T arr[], int len)
{
    
    
	for (int i = 0; i < len; i++)
	{
    
    
		int max = i;
		for (int j = i + 1; j < len; j++)
		{
    
    
			if (arr[max] < arr[j])	max = j;
		}
		if (max != i) mySwap(arr[max], arr[i]);
	}
}

template<typename T> void printArray(T arr[], int len)
{
    
    
	for (int i = 0; i < len; i++) cout << arr[i] << ' ';
	cout << endl;
}

void test01()
{
    
    
	char charArr[] = "bdcfeagh";
	int number = sizeof(charArr) / sizeof(char);
	mySort(charArr, number);
	printArray(charArr, number);
}

void test02()
{
    
    
	int intArr[] = {
    
     7, 5, 8, 1, 3, 9, 2, 4, 6 };
	int num = sizeof(intArr) / sizeof(int);
	mySort(intArr, num);
	printArray(intArr, num);
}

1.2 The difference between ordinary functions and function templates

The difference between ordinary functions and function templates:

  • When the function template is called, if automatic type deduction is used, no implicit type conversion will occur

  • Implicit type conversions can occur if the explicit type is specified

  • Automatic type conversion (implicit type conversion) can occur when ordinary function calls

#include <iostream>
using namespace std;

int myAdd01(int a, int b)
{
    
    
	return a + b;
}

template<typename T> T myAdd02(T a, T b)
{
    
    
	return a + b;
}

void test01()
{
    
    
	int a = 10, b = 20;
	char c = 'c';  // c 对应的ASCII码 99

	// 要注意和实参被 & 修饰的区别:如果是引用修饰的实参,这里类型不一致也会报错
	cout << myAdd01(a, c) << endl;  // 109

	// 自动类型转换,不会发生隐式类型转换
	// cout << myAdd02(a, c) << endl;  // 使用模板,不会发生自动类型转换,这里报错

	// 显示指定类型
	cout << myAdd02<int>(a, c) << endl;  // 这里如果指定类型,则不会报错
} 

1.3 Calling rules of ordinary functions and function templates

  1. If both the function template and the normal function can be implemented, the normal function will be called first. If there is only a statement but no function body implementation, an error will be reported
  2. Function templates can be forced to be invoked with an empty template parameter list
  3. Function templates can also be overloaded, as long as their formal parameter lists or type parameter lists are different
  4. If the function template can produce a better match, the function template is called first

The calling order of function templates and functions:

  1. First find a normal function with parameters that match exactly
  2. Then find a template function with parameters that completely match
  3. Then find an ordinary function that can match the actual parameter after automatic type conversion
  4. found all, error
// 普通函数与函数模板调用规则
void myPrint(int a, int b)
{
    
    
	cout << "调用的普通函数" << endl;
}

template<typename T> void myPrint(T a, T b) 
{
    
     
	cout << "调用的模板" << endl;
}

template<typename T> void myPrint(T a, T b, T c) 
{
    
     
	cout << "调用重载的模板" << endl; 
}

void test01()
{
    
    
	// 1、如果函数模板和普通函数都可以实现,优先调用普通函数
	// 注意 如果告诉编译器,普通函数是有的,但只是声明没有实现,或者不在当前文件内实现,就会报错找不到
	int a = 10;
	int b = 20;
	myPrint(a, b);  // 调用普通函数

	// 2、可以通过空模板参数列表来强制调用函数模板
	myPrint<>(a, b);  // 调用函数模板

	// 3、函数模板也可以发生重载
	int c = 30;
	myPrint(a, b, c);  // 调用重载的函数模板

	// 4、 如果函数模板可以产生更好的匹配,优先调用函数模板
	char c1 = 'a';  // 因为这里 char 类型的会被
	char c2 = 'b';
	myPrint(c1, c2);  // 调用函数模板
}

Summary: Since function templates are provided, it is best not to provide ordinary functions, otherwise ambiguity will easily appear


1.4 Limitations of templates

  • The generality of the template is not a panacea. If the parameters passed in in the following example are two arrays, it cannot be realized
template<class T>
void f(T a, T b)
{
    
     
	if(a > b) {
    
     ... }
}
  • In the above code, if the data type of T is passed in a custom data type like Person, it will not work properly. Therefore, in order to solve this problem, C++ provides template overloading, which can provide specific templates for these specific types
#include<iostream>
#include <string>

using namespace std;

class Person
{
    
    
public:
	Person(string name, int age)
	{
    
    
		this->m_Name = name;
		this->m_Age = age;
	}
	string m_Name;
	int m_Age;
};

// 普通函数模板
template<class T> bool myCompare(T& a, T& b)
{
    
    
	if (a == b)	return true;
	else	return false;
}

// 具体化,优先于普通模板。以 template<> 开头,并通过名称指出类型
template<> bool myCompare(Person& p1, Person& p2)
{
    
    
	if (p1.m_Name  == p2.m_Name && p1.m_Age == p2.m_Age)	return true;
	else	return false;
}

void test01()
{
    
    
	int a = 10;
	int b = 20;
	// 内置数据类型可以直接使用通用的函数模板
	bool ret = myCompare(a, b);
	if (ret)	cout << "a == b " << endl;
	else	cout << "a != b " << endl;
}

void test02()
{
    
    
	Person p1("Tom", 10);
	Person p2("Tom", 10);
	// 自定义数据类型,不会调用普通的函数模板
	// 可以创建具体化的 Person 数据类型的模板,用于特殊处理这个类型
	bool ret = myCompare(p1, p2);
	if (ret)	cout << "p1 == p2 " << endl;
	else	cout << "p1 != p2 " << endl;
}

Summarize:

  • Generalization of custom types can be solved by using concrete templates
  • Learning templates is not to write templates, but to use the templates provided by the system in STL

2 types of templates

  • Function: Create a general class, the member data type in the class can not be specified, and a virtual type is used to represent it
template<typename T>
#include <iostream>
#include <string>

using namespace std;

// 类模板
template<class NameType, class AgeType> class Person
{
    
    
public:
	Person(NameType name, AgeType age)  // 使用构造函数赋初值
	{
    
    
		this->mName = name;
		this->mAge = age;
	}
	void showPerson()
	{
    
    
		cout << "name: " << this->mName << " age: " << this->mAge << endl;
	}
public:
	NameType mName;
	AgeType mAge;
};

void test01()
{
    
    
	// 指定 NameType 为 string 类型,AgeType 为 int 类型
	Person<string, int> p("孙悟空", 99);
	P1.showPerson();
}

2.1 Difference between class template and function template

There are two main differences between class templates and function templates:

  1. There is no way to use automatic type deduction for class templates (that is, they cannot be automatically converted in the parameter list, such as an int and a double), and the type must be specified explicitly
  2. Class templates can have default parameters in the template parameter list
#include <iostream>
#include <string>

using namespace std;

// 类模板
template<class NameType, class AgeType = int>  class Person
{
    
    
public:
	Person(NameType name, AgeType age)
	{
    
    
		this->mName = name;
		this->mAge = age;
	}
	void showPerson()
	{
    
    
		cout << "name: " << this->mName << " age: " << this->mAge << endl;
	}
public:
	NameType mName;
	AgeType mAge;
};

// 1、类模板没有自动类型推导的使用方式
void test01()
{
    
    
	// Person p("孙悟空", 1000); // 错误;类模板使用时候,不可以用自动类型推导
	Person<string, int> p("孙悟空", 1000);  // 必须使用显示指定类型的方式,使用类模板
	p.showPerson();
}

// 2、类模板在模板参数列表中可以有默认参数
void test02()
{
    
    
	Person<string> p("猪八戒", 999);  // 类模板中的模板参数列表,可以指定默认参数
	p.showPerson();
}

2.2 Timing of creating member functions in class templates

The difference between the creation timing of member functions in class templates and member functions in ordinary classes:

  • Member functions in ordinary classes can be created at the outset
  • Member functions in class templates are created when they are called
class Person1
{
    
    
public:
	void showPerson1()
	{
    
    
		cout << "Person1 show" << endl;
	}
};

class Person2
{
    
    
public:
	void showPerson2()
	{
    
    
		cout << "Person2 show" << endl;
	}
};

template<class T> class MyClass
{
    
    
public:
	T obj;

	// 可以编译成功
	// 类模板中的成员函数,并不是一开始就创建的,而是在模板调用时再生成
	void fun1() {
    
    
		obj.showPerson1();
	}
	void fun2() {
    
    
		obj.showPerson2();  // Person1 中没有 showPerson2()
	}
};

void test01()
{
    
    
	MyClass<Person1> m;  // 说明 m 是 Person1 的对象

	m.fun1();
	// showPerson2 不是 showPerson1 的成员
	// m.fun2();  // 编译会出错,说明函数调用才会去创建成员函数
}

2.3 Class template object as function parameter

There are three input methods:

  1. Specifies the incoming type—directly explicit object data type
  2. Parameter templating—turn the parameters in the object into templates for passing
  3. Entire class templated — templates this object type for passing
#include <iostream>
#include <string>

using namespace std;

// 类模板
template<class NameType, class AgeType = int> class Person
{
    
    
public:
	Person(NameType name, AgeType age)
	{
    
    
		this->mName = name;
		this->mAge = age;
	}

	void showPerson()
	{
    
    
		cout << "name: " << this->mName << " age: " << this->mAge << endl;
	}

public:
	NameType mName;
	AgeType mAge;
};

// 1、指定传入的类型(最常用)
void printPerson1(Person<string, int>& p)
{
    
    
	p.showPerson();
}

void test01()
{
    
    
	Person<string, int > p("孙悟空", 100);
	printPerson1(p);
}

// 2、参数模板化
template <class T1, class T2> void printPerson2(Person<T1, T2>& p)
{
    
    
	p.showPerson();
	cout << "T1的类型为: " << typeid(T1).name() << endl;  // string
	cout << "T2的类型为: " << typeid(T2).name() << endl;  // int
}
void test02()
{
    
    
	Person<string, int> p("猪八戒", 90);
	printPerson2(p);
}

// 3、整个类模板化
template<class T> void printPerson3(T& p)
{
    
    
	cout << "T的类型为: " << typeid(T).name() << endl;  // Person string int
	p.showPerson();

}
void test03()
{
    
    
	Person<string, int> p("唐僧", 30);
	printPerson3(p);
}

2.4 Class Templates and Inheritance

When class templates encounter inheritance, you need to pay attention to:

  • When the parent class inherited by the subclass is a class template, the subclass must specify the type of T in the parent class when declaring it. If not specified, the compiler cannot allocate memory to the subclass
  • If you want to flexibly specify the type of T in the parent class, the subclass also needs to become a class template
template<class T> class Base
{
    
    
	T m;
};

// 错误,c++ 编译需要给子类分配内存,必须知道父类中 T 的类型才可以向下继承
// class Son: public Base
class Son: public Base<int>  // 必须指定一个类型
{
    
    
};

void test01()
{
    
    
	Son c;
}

// 如果想灵活指定出父类中 T 的类型,子类也需变为类模板
// 类模板继承类模板,用 T2 指定父类中的 T 类型
template<class T1, class T2> class Son2: public Base<T2>
{
    
    
public:
	Son2()
	{
    
    
		cout << typeid(T1).name() << endl;  // int
		cout << typeid(T2).name() << endl;  // char
	}
};

void test02()
{
    
    
	Son2<int, char> child1;
}

2.5 Out-of-class implementation of class template member functions

#include <string>

// 类模板中成员函数类外实现
template<class T1, class T2> class Person {
    
    
public:
	// 成员函数类内声明
	Person(T1 name, T2 age);
	void showPerson();
public:
	T1 m_Name;
	T2 m_Age;
};

// 构造函数类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
    
      // Person<T1, T2>:: 指定作用域(也就是谁的构造函数)
	this->m_Name = name;
	this->m_Age = age;
}

// 成员函数类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson() {
    
    
	cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}

void test01()
{
    
    
	Person<string, int> p("Tom", 20);
	p.showPerson();
}

2.6 class template file writing

  • If there are multiple classes, the classes are usually written in a separate file

  • Problem: The creation time of the member function in the class template is in the call phase, resulting in the link not being able to be written when the sub-file is written

  • solution:

    • Solution 1: Directly include the .cpp source file (generally not used)
    • Solution 2: It is not mandatory to write the declaration and implementation of the class into a file named .hpp

person.hpp

#pragma once
#include <iostream>
#include <string>

using namespace std;

template<class T1, class T2> class Person {
    
    
public:
	Person(T1 name, T2 age);
	void showPerson();
public:
	T1 m_Name;
	T2 m_Age;
};

// 构造函数类外实现
template<class T1, class T2> Person<T1, T2>::Person(T1 name, T2 age) {
    
    
	this->m_Name = name;
	this->m_Age = age;
}

// 成员函数类外实现
template<class T1, class T2> void Person<T1, T2>::showPerson() {
    
    
	cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}

main.cpp

#include <iostream>
using namespace std;

// 解决方式1,包含cpp源文件
#include "person.cpp" 

// 解决方式2,将声明和实现写到一起,文件后缀名改为.hpp
#include "person.hpp"

void test01()
{
    
    
	Person<string, int> p("Tom", 10);
	p.showPerson();
}

2.7 Class templates and friends

  • Implementation of global functions within the class - just declare friends directly within the class (this is best)

  • Out-of-class implementation of global functions - need to let the compiler know the existence of global functions in advance

#include <iostream>
#include <string>

using namespace std;

// 2、全局函数配合友元  类外实现 - 先做函数模板声明,下方在做函数模板定义,在做友元
template<class T1, class T2> class Person;

// 类模板函数定义,因为这里需要 Person 类,所以,需要在该定义之前,声明 Person 类
template<class T1, class T2> void printPerson2(Person<T1, T2>& p)  // 全局函数,没有必要加作用域
{
    
    
	cout << "类外实现 ---- 姓名: " << p.m_Name << " 年龄:" << p.m_Age << endl;
}

template<class T1, class T2> class Person
{
    
    
	// 1、全局函数配合友元   类内实现
	friend void printPerson(Person<T1, T2> p)
	{
    
    
		cout << "姓名: " << p.m_Name << " 年龄:" << p.m_Age << endl;
	}

	// 2、全局函数配合友元  类外实现
	// 全局函数类外实现,需要提前让编译器知道全局函数的存在
	friend void printPerson2<>(Person<T1, T2>& p);  // 必须要加空模板参数列表,否则只是普通函数,与类外的函数定义不匹配

public:
	Person(T1 name, T2 age)
	{
    
    
		this->m_Name = name;
		this->m_Age = age;
	}

private:
	T1 m_Name;
	T2 m_Age;
};

// 1、全局函数在类内实现
void test01()
{
    
    
	Person<string, int> p("Tom", 20);
	printPerson(p);
}

// 2、全局函数在类外实现
void test02()
{
    
    
	Person<string, int> p("Jerry", 30);
	printPerson2(p);
}

3 Introduction to STL

3.1 The birth of STL

  • The software world has long wanted to build something reusable
  • C++'s object-oriented and generic programming ideas aim to improve reusability
  • In most cases, there is no set of standards for data structures and algorithms, resulting in a lot of repetitive work
  • In order to establish a set of standards for data structures and algorithms, STL was born

3.2 Basic Concepts of STL

  • STL (Standard Template Library, standard template library )
  • STL is broadly divided into: container (container), algorithm (algorithm), iterator (iterator)
  • Seamless connection between container and algorithm through iterator
  • Almost all code in STL uses template classes or template functions

3.3 Six components of STL

  1. Container : various data structures, such as vector, list, deque, set, map, etc., used to store data
  2. Algorithms : various commonly used algorithms, such as sort, find, copy, for_each, etc.
  3. Iterator : acts as the glue between the container and the algorithm
  4. Functor: Behaves like a function and can be used as some kind of strategy for an algorithm
  5. Adapter: something that modifies a container or a functor or iterator interface
  6. Space configurator: responsible for space configuration and management

3.4 Containers, Algorithms, Iterators in STL

  • Container: a place for storage

  • The STL container is to realize some of the most widely used data structures, commonly used data structures: arrays, linked lists, trees, stacks, queues, sets, mapping tables, etc. These containers are divided into two types: sequential containers and associative containers.

    • Sequential container: Emphasizes the ordering of values, and each element in the sequential container has a fixed position
    • Associative container: binary tree structure, there is no strict physical order relationship between elements (but there may be a possibility of sorting values)
  • Algorithm: the solution to the problem

  • Limited steps to solve logical or mathematical problems are called algorithms (Algorithms). The algorithm is divided into: qualitative change algorithm and non-qualitative change algorithm

    • Qualitative change algorithm: It means that the content of the elements in the interval will be changed during the operation , such as copying, replacing, deleting, etc.
    • Non-qualitative algorithm: It means that the content of elements in the interval will not be changed during the operation , such as searching, counting, traversing, finding extreme values, etc.
  • Iterator: The glue between the container and the algorithm, providing a way to sequentially search for the elements contained in a container without exposing the internal representation of the container

  • Each container has its own iterator, which is very similar to a pointer

Iterator type:

type Function Support operation
input iterator read-only access to data Read-only, supports ++, ==, ! =
output iterator write-only access to data write only, supports ++
forward iterator read and write operations, and can advance the iterator Read and write, support ++, ==,! =
bidirectional iterator Read and write operations, and can operate forward and backward
Read and write operations, can access arbitrary data in a jumping way, the most powerful iterator Read and write, support ++, –,
random access iterator Read and write, support ++, –, [n], -n, <, <=, >, >=

The relationship between the three: the algorithm needs an iterator to access the elements in the container


3.5 Introduction to container algorithm iterators

The most commonly used container in STL is Vector, which can be understood as an array

container: vector

Algorithm: for_each

Iterator: vector::iterator

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

void MyPrint(int val)
{
    
    
	cout << val << endl;
}

void test01()
{
    
    
	// 创建 vector 类型的容器对象,并且通过模板参数指定容器中存放的数据的类型
	vector<int> v;
	// 向容器中放数据
	v.push_back(10);
	v.push_back(20);
	v.push_back(30);
	v.push_back(40);

	// v.begin() 起始迭代器:返回指向容器中的第一个数据
	// v.end() 结束迭代器:返回指向容器的最后一个元素的下一个位置
	// vector<int>::iterator 中的 iterator 是迭代器名称

	vector<int>::iterator pBegin = v.begin();
	vector<int>::iterator pEnd = v.end();

	// 第一种遍历方式
	while (pBegin != pEnd)	cout << (*pBegin) ++ << endl;

	// 第二种遍历方式
	for (vector<int>::iterator it = v.begin(); it != v.end(); it ++)
		cout << *it << endl;

	// 第三种遍历方式
	// 使用 STL 提供标准遍历算法  头文件 algorithm
	for_each(v.begin(), v.end(), MyPrint);  // 写函数名即可,因为利用了回调机制
}

Store custom data types in vector

#include <vector>
#include <string>

// 自定义数据类型
class Person
{
    
    
public:
	Person(string name, int age) {
    
    
		mName = name;
		mAge = age;
	}
public:
	string mName;
	int mAge;
};

// 存放自定义数据类型对象
void test01()
{
    
    
	vector<Person> v;

	// 创建数据
	Person p1("aaa", 10);
	Person p2("bbb", 20);
	Person p3("ccc", 30);
	Person p4("ddd", 40);
	Person p5("eee", 50);

	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	v.push_back(p5);

	// 两种遍历方式都行
	// for (vector<Person>::iterator it = v.begin(); it != v.end(); it++) cout << "Name:" << (*it).mName << " Age:" << (*it).mAge << endl;
	for (vector<Person>::iterator it = v.begin(); it != v.end(); it++) cout << "Name:" << it->mName << " Age:" << it->mAge << endl;
}

// 存放自定义数据类型指针
void test02()
{
    
    
	vector<Person*> v;

	// 创建数据
	Person p1("aaa", 10);
	Person p2("bbb", 20);
	Person p3("ccc", 30);
	Person p4("ddd", 40);
	Person p5("eee", 50);

	// 向容器中添加指针数据
	v.push_back(&p1);
	v.push_back(&p2);
	v.push_back(&p3);
	v.push_back(&p4);
	v.push_back(&p5);

	// 遍历容器中的数据,看 <> 中的是啥类型,取出来的就是啥类型的数据
	for (vector<Person*>::iterator it = v.begin(); it != v.end(); it++) {
    
    
		Person* p = (*it);
		cout << "Name:" << p->mName << " Age:" << (*it)->mAge << endl;
	}
}

Vector container nested container, similar to a two-dimensional array

#include <vector>

// 容器嵌套容器
void test01()
{
    
    
	vector<vector<int>> v;

	// 创建小容器
	vector<int> v1;
	vector<int> v2;
	vector<int> v3;
	vector<int> v4;

	// 向小容器中添加数据
	for (int i = 0; i < 4; i++)
	{
    
    
		v1.push_back(i + 1);  // 1 2 3 4
		v2.push_back(i + 2);  // 2 3 4 5
		v3.push_back(i + 3);  // 3 4 5 6
		v4.push_back(i + 4);  // 4 5 6 7
	}

	// 将小容器元素插入到大容器中
	v.push_back(v1);
	v.push_back(v2);
	v.push_back(v3);
	v.push_back(v4);

	// 因为 *it 对应的 <> 也是一个小容器,所以需要再次遍历
	for (vector<vector<int>>::iterator it = v.begin(); it != v.end(); it++) {
    
    
		for (vector<int>::iterator vit = (*it).begin(); vit != (*it).end(); vit++) {
    
    
			cout << *vit << " ";
		}
		cout << endl;
	}
}

4 common containers

4.1 string container

Nature:

  • string is a C++-style string, and string is essentially a class

The difference between string and char*:

  • char * is a pointer
  • string is a class, which encapsulates char* inside the class, and * manages this string, which is a container of char type

Features:

The string class internally encapsulates many member methods

For example: find find, copy copy, delete delete, replace replace, insert insert

String manages the memory allocated by char*, so there is no need to worry about copying out of bounds and value out of bounds, etc., and the class is responsible for it


4.1.1 The string constructor

  • string();// create an empty string eg: string str;
  • string(const char* s);​** ​**// Initialize with string s
  • string(const string& str);// use a string object to initialize another string object
  • string(int n, char c);// initialize with n characters c
#include <string>

// string 构造
void test01()
{
    
    
	string s1;  // 创建空字符串,调用无参构造函数
	const char* str = "hello world";

	string s2(str);  // 把 c_string 转换成了string

	cout << "str2 = " << s2 << endl;  // hello world

	string s3(s2);  // 调用拷贝构造函数
	cout << "str3 = " << s3 << endl;  // hello world

	string s4(10, 'a');
	cout << "str3 = " << s3 << endl;  // aaaaaaaaaa
}

4.1.2 String assignment operation

The function prototype of the assignment:

  • string& operator=(const char* s);// char* type string is assigned to the current string
  • string& operator=(const string &s);// Assign the string s to the current string
  • string& operator=(char c);// Characters are assigned to the current string
  • string& assign(const char *s);// Assign the string s to the current string
  • string& assign(const char *s, int n);// Assign the first n characters of the string s to the current string
  • string& assign(const string &s);// Assign the string s to the current string
  • string& assign(int n, char c);// Assign n characters c to the current string
//赋值
void test01()
{
    
    
	string str1;
	str1 = "hello world";
	cout << "str1 = " << str1 << endl;  // hello world

	string str2;
	str2 = str1;
	cout << "str2 = " << str2 << endl;  // hello world

	string str3;
	str3 = 'a';  // 用的比较少
	cout << "str3 = " << str3 << endl;  // a

	string str4;
	str4.assign("hello c++");
	cout << "str4 = " << str4 << endl;  // hello c++

	string str5;
	str5.assign("hello c++",5);
	cout << "str5 = " << str5 << endl;  // hello

	string str6;
	str6.assign(str5);
	cout << "str6 = " << str6 << endl;  // hello

	string str7;
	str7.assign(5, 'x');
	cout << "str7 = " << str7 << endl;  // xxxxx
}

4.1.3 string concatenation

Function prototype:

  • string& operator+=(const char* str);// Overload the += operator
  • string& operator+=(const char c);// Overload the += operator
  • string& operator+=(const string& str);// Overload the += operator
  • string& append(const char *s);// Connect the string s to the end of the current string
  • string& append(const char *s, int n);// Connect the first n characters of the string s to the end of the current string
  • string& append(const string &s); // 同 operator+=(const string& str)
  • string& append(const string &s, int pos, int n);// The n characters starting from pos in the string s are connected to the end of the string
void test01()
{
    
    
	string str1 = "我";

	str1 += "爱玩游戏";

	cout << "str1 = " << str1 << endl;  // str1 = 我爱玩游戏

	str1 += ':';

	cout << "str1 = " << str1 << endl;  // str1 = 我爱玩游戏:

	string str2 = "LOL DNF";

	str1 += str2;

	cout << "str1 = " << str1 << endl;  // str1 = 我爱玩游戏:LOL DNF

	string str3 = "I";
	str3.append(" love ");
	str3.append("game abcde", 4);  // I love game
	// str3.append(str2);
	str3.append(str2, 4, 3);  // 从下标 4 位置开始 ,截取 3 个字符,拼接到字符串末尾
	cout << "str3 = " << str3 << endl;  // I love gameDNF
}

4.1.4 string find and replace

  • Find: Find whether the specified string exists
  • Replace: replace the string at the specified position

Function prototype:

  • int find(const string& str, int pos = 0) const;// Find the position of the first occurrence of str, starting from pos
  • int find(const char* s, int pos = 0) const;// Find the first occurrence of s, starting from pos
  • int find(const char* s, int pos, int n) const;// Find the first position of the first n characters of s from pos position
  • int find(const char c, int pos = 0) const;// Find the first occurrence of character c
  • int rfind(const string& str, int pos = npos) const;// Find the last position of str, starting from pos
  • int rfind(const char* s, int pos = npos) const;// Find the last occurrence of s, starting from pos
  • int rfind(const char* s, int pos, int n) const;// Find the last position of the first n characters of s from pos
  • int rfind(const char c, int pos = 0) const;// Find the last occurrence of character c
  • string& replace(int pos, int n, const string& str);// Replace n characters starting from pos with the string str
  • string& replace(int pos, int n,const char* s);// Replace n characters starting from pos with string s
// 查找
void test01()
{
    
    
	string str1 = "abcdefgde";

	// 从左往右查
	int pos = str1.find("de");  // 如果正确查找,返回下标;否则,返回 -1

	// 未找到
	if (pos == -1) cout << "未找到" << endl;
	// 正确查找
	else cout << "pos = " << pos << endl;

	pos = str1.rfind("de");  // 从右往左查找

	cout << "pos = " << pos << endl;
}

// 替换
void test02()
{
    
    
	string str1 = "abcdefgde";
	str1.replace(1, 3, "1111");  // 从下标 1 起 3 个字符,替换为 1111

	cout << "str1 = " << str1 << endl;  // a1111efg
}

Summarize:

  • find searches from left to back, rfind from right to left
  • find Returns the position of the first character found after finding the string, and returns -1 if not found
  • replace When replacing, specify from which position, how many characters, and what kind of string to replace

4.1.5 string string comparison

  • Comparison method: compare according to the ASCII code of the character

equals = returns 0

greater than > returns 1

less than < returns -1

Function prototype:

  • int compare(const string& s) const;// compare with string s
  • int compare(const char* s) const;// compare with string s
// 字符串比较
void test01()
{
    
    
	string s1 = "hello";
	string s2 = "aello";

	int ret = s1.compare(s2);

	if (ret == 0) 	cout << "s1 等于 s2" << endl;
	else if (ret > 0) 	cout << "s1 大于 s2" << endl;
	else 	cout << "s1 小于 s2" << endl;
}

Summary: String comparison is mainly used to compare whether two strings are equal , and it is not very meaningful to judge who is bigger and who is smaller


4.1.6 string character access

There are two ways to access a single character in string

  • char& operator[](int n);// Get characters by []
  • char& at(int n);// get character by at method
void test01()
{
    
    
	string str = "hello world";

	// 通过 [] 方式取字符
	for (int i = 0; i < str.size(); i++)	cout << str[i] << " ";  // h e l l o
	cout << endl;

	// 通过 at 方法获取字符
	for (int i = 0; i < str.size(); i++)	cout << str.at(i) << " ";  // h e l l o
	cout << endl;

	// 修改单个字符
	str[0] = 'x';
	str.at(1) = 'x';
	cout << str << endl;  // x x l l o
}

4.1.7 string insertion and deletion

Function prototype:

  • string& insert(int pos, const char* s);// insert string
  • string& insert(int pos, const string& str);// insert string
  • string& insert(int pos, int n, char c);// Insert n characters at the specified position c
  • string& erase(int pos, int n = npos);// Delete n characters starting from Pos
// 字符串插入和删除
void test01()
{
    
    
	string str = "hello";
	str.insert(1, "111");

	cout << str << endl;  // h111ello

	str.erase(1, 3);  // 从 1 号位置开始删除 3 个字符
	cout << str << endl;  // hello
}

4.1.8 string get substring

Function prototype:

string substr(int pos = 0, int n = npos) const;// return a string consisting of n characters starting from pos

// 子串
void test01()
{
    
    
	string str = "abcdefg";
	string subStr = str.substr(1, 3);
	cout << "subStr = " << subStr << endl;  // bcd

	string email = "[email protected]";
	int pos = email.find("@");  // 按字符查找,返回开始的下标
	cout << pos << endl;  // 8
	string username = email.substr(0, pos);  // 截取名字子串
	cout << "username: " << username << endl;  // username: zhangsan
}

4.2 vector containers

  • Function: The vector data structure is very similar to an array, also known as a single-ended array
  • The difference between vector and ordinary array: the difference is that the array is a static space, while the vector can be dynamically expanded
  • Dynamic expansion: instead of adding a new space after the original space, a larger memory space is found, and then the original data is copied to the new space to release the original space; the iterator of the vector container supports random access

insert image description here


4.2.1 vector constructor

Function prototype:

  • vector<T> v;// Use template to implement class implementation, default constructor
  • vector(v.begin(), v.end());// Copy the elements in the interval v[begin(), end()) to itself
  • vector(n, elem);// The constructor copies n elems to itself
  • vector(const vector &vec);// copy constructor
void printVector(vector<int>& v)
{
    
    
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)	cout << *it << " ";
	cout << endl;
}

void test01()
{
    
    
	vector<int> v1;  // 无参构造
	for (int i = 0; i < 10; i++)	v1.push_back(i);

	printVector(v1);  // 0 1 2 3 4 5 6 7 8 9

	// 通过区间方式进行构造
	vector<int> v2(v1.begin(), v1.end());
	printVector(v2);  // 0 1 2 3 4 5 6 7 8 9

	// n 个 elem 方式构造
	vector<int> v3(10, 100);
	printVector(v3);  // 100 100 100 100 100 100 100 100 100 100

	vector<int> v4(v3);
	printVector(v4);  // 100 100 100 100 100 100 100 100 100 100
}

4.2.2 Vector assignment operation

Function prototype:

  • vector& operator=(const vector &vec);// overloaded equals operator
  • assign(beg, end);// Assign the copy of the data in the interval [beg, end) to itself** (note the difference from string here)**
  • assign(n, elem);// Copy and assign n elems to itself
#include <vector>

void printVector(vector<int>& v)
{
    
    
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)	cout << *it << " ";
	cout << endl;
}

// 赋值操作
void test01()
{
    
    
	vector<int> v1;  // 无参构造
	for (int i = 0; i < 10; i++)	v1.push_back(i);
	printVector(v1);  // 0 1 2 3 4 5 6 7 8 9

	// 赋值
	vector<int> v2;
	v2 = v1;
	printVector(v2);  // 0 1 2 3 4 5 6 7 8 9

	vector<int> v3;
	v3.assign(v1.begin(), v1.end());
	printVector(v3);  // 0 1 2 3 4 5 6 7 8 9

	vector<int> v4;
	v4.assign(10, 100);
	printVector(v4);  // 100 100 100 100 100 100 100 100 100 100
}

4.2.3 Vector capacity and size

Function prototype:

  • empty();// Check if the container is empty
  • capacity();// The capacity of the container (randomly opened)
  • size();// returns the number of elements in the container
  • resize(int 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
  • resize(int num, elem);// Re-designate the length of the container as num. If the container becomes longer, fill the new position with elem value. If the container gets shorter, elements at the end that exceed the length of the container are removed
#include <vector>

void printVector(vector<int>& v)
{
    
    
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)	cout << *it << " ";
	cout << endl;
}

void test01()
{
    
    
	vector<int> v1;
	for (int i = 0; i < 10; i++)	v1.push_back(i);
	printVector(v1);  // 0 1 2 3 4 5 6 7 8 9

	// 容器为空
	if (v1.empty())	cout << "v1 为空" << endl;
	else
	{
    
    
		cout << "v1 不为空" << endl;
		cout << "v1 的容量 = " << v1.capacity() << endl;  // 13
		cout << "v1 的大小 = " << v1.size() << endl;  // 10
	}

	// 重新指定大小,若指定的更大,用 10 填充,默认用 0 填充新位置
	v1.resize(15, 10);
	printVector(v1);  // 0 1 2 3 4 5 6 7 8 9 10 10 10 10 10

	// 重新指定大小 ,若指定的更小,超出部分元素被删除
	v1.resize(5);
	printVector(v1);  // 0 1 2 3 4
}

4.2.4 Vector insertion and deletion

Function prototype:

  • push_back(ele);// Insert element ele at the end
  • pop_back();// delete the last element
  • insert(const_iterator pos, ele);// The iterator points to the position pos to insert the element ele
  • insert(const_iterator pos, int count,ele);// The iterator points to position pos and inserts count elements ele
  • erase(const_iterator pos);// delete the element pointed to by the iterator
  • erase(const_iterator start, const_iterator end);// Delete the elements from the iterator from start to end
  • clear();// delete all elements in the container

#include <vector>

void printVector(vector<int>& v)
{
    
    
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)	cout << *it << " ";
	cout << endl;
}

// 插入和删除
void test01()
{
    
    
	vector<int> v1;
	//尾插
	v1.push_back(10);
	v1.push_back(20);
	v1.push_back(30);
	v1.push_back(40);
	v1.push_back(50);
	printVector(v1);  // 10 20 30 40 50

	// 尾删
	v1.pop_back();
	printVector(v1);  // 10 20 30 40

	// 插入
	v1.insert(v1.begin(), 100);  // 把 100 插入到第一个参数的位置
	printVector(v1);  // 100 10 20 30 40

	v1.insert(v1.begin(), 2, 1000);  // 限定插入长度
	printVector(v1);  // 1000 1000 100 10 20 30 40

	// 删除,参数也是迭代器
	v1.erase(v1.begin());
	printVector(v1);  // 1000 100 10 20 30 40

	// 清空
	v1.erase(v1.begin(), v1.end());  // 从头到尾全部删除
	v1.clear();  // 不用提供参数
	printVector(v1);
}

4.2.5 vector data access

Function prototype:

  • at(int idx);// Return the data pointed to by the index idx
  • operator[];// Return the data pointed to by the index idx
  • front();// return the first data element in the container
  • back();// Return the last data element in the container
#include <vector>

void test01()
{
    
    
	vector<int> v1;
	for (int i = 0; i < 10; i++)	v1.push_back(i);

	// 利用 [] 访问数组中元素
	for (int i = 0; i < v1.size(); i++)	cout << v1[i] << " ";
	cout << endl;

	// 利用 at 方式访问元素
	for (int i = 0; i < v1.size(); i++)	cout << v1.at(i) << " ";
	cout << endl;

	cout << "v1 的第一个元素为: " << v1.front() << endl;  // 0
	cout << "v1 的最后一个元素为: " << v1.back() << endl;  // 9
}

4.2.6 vector swap container

Function prototype:

  • swap(vec); ​// Swap vec with its own elements (practical use: use swap to shrink memory space)
#include <vector>

void printVector(vector<int>& v)
{
    
    
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)	cout << *it << " ";
	cout << endl;
}

void test01()
{
    
    
	vector<int> v1;
	for (int i = 0; i < 10; i++)	v1.push_back(i);
	printVector(v1);  // 0 1 2 3 4 5 6 7 8 9

	vector<int> v2;
	for (int i = 10; i > 0; i--)	v2.push_back(i);
	printVector(v2);  // 10 9 8 7 6 5 4 3 2 1

	// 互换容器
	cout << "互换后" << endl;
	v1.swap(v2);
	printVector(v1);  // 10 9 8 7 6 5 4 3 2 1
	printVector(v2);  // 0 1 2 3 4 5 6 7 8 9
}

// 实际用途:使用 swap 可以收缩内存空间
void test02()
{
    
    
	vector<int> v;
	for (int i = 0; i < 100000; i++)	v.push_back(i);

	cout << "v的容量为:" << v.capacity() << endl;  // 138255
	cout << "v的大小为:" << v.size() << endl;  // 100000

	v.resize(3);  // 重新指定大小

	cout << "v 的容量为:" << v.capacity() << endl;  // 138255
	cout << "v 的大小为:" << v.size() << endl;  // 3

	// 收缩内存
	vector<int>(v).swap(v);
	// vector<int>(v) -- 匿名对象:当前行使用完,系统立马回收内存空间

	cout << "v 的容量为:" << v.capacity() << endl;  // 3
	cout << "v 的大小为:" << v.size() << endl;  // 3
}

Summary: swap can make two containers interchangeable, which can achieve a practical effect of shrinking memory


4.2.7 vector reserved space

  • Function: reduce the number of vector expansions when dynamically expanding capacity

Function prototype:

  • reserve(int len);// The container reserves len element length, the reserved position is not initialized, and the element is inaccessible
#include <vector>

void test01()
{
    
    
	vector<int> v;

	// 预留空间
	v.reserve(100000);

	int num = 0;  // 统计开辟次数
	int* p = NULL;

	// 这里说的是,在遍历创建容器的时候,并不是一次性就把内存空间确定好了的
	// 因为 vector 会动态扩展,如果内存满了的话,下面的遍历会进行多次开辟内存操作
	for (int i = 0; i < 100000; i++) {
    
    
		v.push_back(i);
		if (p != &v[0]) {
    
    
			p = &v[0];
			num++;
		}
	}

	// 如果没预留空间的话,num 是 30,开辟了 30 次内存空间
	// 如果预留空间,num 是 1,只开辟了一次内存空间
	cout << "num:" << num << endl;
}

Summary: If the amount of data is large, you can use reserve to reserve space at the beginning


4.3 deque container

Function:

  • Double-ended array, which can perform insertion and deletion operations on the head end

Features:

  • Support random access

The difference between deque and vector:

  • The insertion and deletion efficiency of vector for the head is low, and the larger the data volume, the lower the efficiency
  • Relatively speaking, deque inserts and deletes the head faster than vector
  • Vector accesses elements faster than deque (vector is a contiguous memory space)

insert image description here

How a deque works internally:

  • There is a central controller inside the deque , which maintains the contents of each buffer, and stores real data in the buffer
  • The central controller maintains the address of each buffer, making deque like a continuous memory space

insert image description here


4.3.1 deque constructor

Function prototype:

  • deque<T> deqT;// default constructor
  • deque(beg, end);// The constructor copies the elements in the range [beg, end) to itself
  • deque(n, elem);// The constructor copies n elems to itself
  • deque(const deque &deq);// copy constructor
#include <deque>

void printDeque(const deque<int>& d)  // 限定只读状态
{
    
    
	// 不能从 const iterator -> iterator,所以,需要更改 const_iterator
	// 添加了 const 之后,元素的值就不能修改了
	for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++)
		cout << *it << " ";
	cout << endl;
}

// deque 构造
void test01()
{
    
    
	deque<int> d1;  // 无参构造函数
	for (int i = 0; i < 10; i++)	d1.push_back(i);
	printDeque(d1);  // 0 1 2 3 4 5 6 7 8 9

	// 区间方式构造
	deque<int> d2(d1.begin(), d1.end());
	printDeque(d2);  // 0 1 2 3 4 5 6 7 8 9

	// n 个 elem 元素拷贝给本身
	deque<int> d3(10,100);
	printDeque(d3);  // 100 100 100 100 100 100 100 100 100 100

	// 拷贝构造
	deque<int> d4 = d3;
	printDeque(d4);  // 100 100 100 100 100 100 100 100 100 100
}

4.3.2 deque assignment operation

Function prototype:

  • deque& operator=(const deque& deq);// overloaded equals operator
  • assign(beg, end);// Assign the copy of the data in the interval [beg, end) to itself
  • assign(n, elem);// Copy and assign n elems to itself
#include <deque>

void printDeque(const deque<int>& d)  // 限定只读状态
{
    
    
	for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
    
    
		cout << *it << " ";
	}
	cout << endl;
}

// 赋值操作
void test01()
{
    
    
	deque<int> d1;
	for (int i = 0; i < 10; i++)	d1.push_back(i);
	printDeque(d1);

	deque<int> d2;
	d2 = d1;
	printDeque(d2);  // 0 1 2 3 4 5 6 7 8 9

	deque<int> d3;
	d3.assign(d1.begin(), d1.end());
	printDeque(d3);  // 0 1 2 3 4 5 6 7 8 9

	deque<int> d4;
	d4.assign(10, 100);
	printDeque(d4);  // 100 100 100 100 100 100 100 100 100 100
}

4.3.3 Deque Size Operations

Function prototype:

  • deque.empty();// Check if the container is empty
  • deque.size();// returns the number of elements in the container
  • deque.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
  • deque.resize(num, elem);// Re-designate the length of the container as num. If the container becomes longer, fill the new position with elem value. If the container gets shorter, elements at the end that exceed the length of the container are removed

Notice:

  • Because deque uses the intermediate control method, it has no capacity, only the size of the container
#include <deque>

void printDeque(const deque<int>& d) 
{
    
    
	for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
    
    
		cout << *it << " ";
	}
	cout << endl;
}

// 大小操作
void test01()
{
    
    
	deque<int> d1;
	for (int i = 0; i < 10; i++)	d1.push_back(i);
	printDeque(d1);

	if (d1.empty())	cout << "d1为空!" << endl;
	else {
    
    
		cout << "d1 不为空!" << endl;
		cout << "d1 的大小为:" << d1.size() << endl;
	}

	// 重新指定大小
	d1.resize(15, 1);
	printDeque(d1);

	d1.resize(5);
	printDeque(d1);
}

4.3.4 deque insertion and deletion

Insert operation at both ends:

  • push_back(elem);// Add a data at the end of the container
  • push_front(elem);// Insert a data at the head of the container
  • pop_back();// Delete the last data in the container
  • pop_front();// delete the first data in the container

Specified position operation:

  • insert(pos,elem);// Insert a copy of the elem element at position pos, return the position of the new data
  • insert(pos,n,elem);// insert n elem data at position pos, no return value
  • insert(pos,beg,end);// Insert the data in the range [beg, end) at position pos, no return value
  • clear();// Clear all data in the container
  • erase(beg,end);// Delete the data in the interval [beg, end) and return the position of the next data
  • erase(pos);// Delete the data at position pos and return the position of the next data
#include <deque>

void printDeque(const deque<int>& d) 
{
    
    
	for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
    
    
		cout << *it << " ";
	}
	cout << endl;
}

// 两端操作
void test01()
{
    
    
	deque<int> d;
	// 尾插
	d.push_back(10);  
	d.push_back(20);
	// 头插
	d.push_front(100);
	d.push_front(200);

	printDeque(d);  // 200 100 10 20 

	// 尾删
	d.pop_back();  // 200 100 10
	// 头删
	d.pop_front();
	printDeque(d);  // 100 10
}

// 插入
void test02()
{
    
    
	deque<int> d;
	d.push_back(10);
	d.push_back(20);
	d.push_front(100);
	d.push_front(200);
	printDeque(d);  // 200 100 10 20 

	d.insert(d.begin(), 1000);
	printDeque(d);  // 1000 200 100 10 20 

	d.insert(d.begin(), 2,10000);
	printDeque(d);  // 10000 10000 1000 200 100 10 20

	deque<int> d2;
	d2.push_back(1);
	d2.push_back(2);
	d2.push_back(3);

	d.insert(d.begin(), d2.begin(), d2.end());
	printDeque(d);  // 1 2 3 10000 10000 1000 200 100 10 20
}

// 删除
void test03()
{
    
    
	deque<int> d;
	d.push_back(10);
	d.push_back(20);
	d.push_front(100);
	d.push_front(200);
	printDeque(d);  // 200 100 10 20

	d.erase(d.begin());  
	printDeque(d);  // 100 10 20

	d.erase(d.begin(), d.end());
	d.clear();
	printDeque(d);
}

4.3.5 deque data access

Function prototype:

  • at(int idx);// Return the data pointed to by the index idx
  • operator[];// Return the data pointed to by the index idx
  • front();// return the first data element in the container
  • back();// Return the last data element in the container
#include <deque>

void printDeque(const deque<int>& d) 
{
    
    
	for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
    
    
		cout << *it << " ";
	}
	cout << endl;
}

// 数据存取
void test01()
{
    
    
	deque<int> d;
	d.push_back(10);
	d.push_back(20);
	d.push_back(30);
	d.push_front(100);
	d.push_front(200);
	d.push_back(300);

	for (int i = 0; i < d.size(); i++)	cout << d[i] << " ";
	cout << endl;

	for (int i = 0; i < d.size(); i++)	cout << d.at(i) << " ";
	cout << endl;

	cout << "front:" << d.front() << endl;  // 300
	cout << "back:" << d.back() << endl;  // 30
}

4.3.6 deque sorting

algorithm:

  • sort(iterator beg, iterator end)// Sort the elements in the beg and end intervals (containers that support random access iterators can use the sort algorithm)
#include <deque>
#include <algorithm>

void printDeque(const deque<int>& d)
{
    
    
	for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++)	cout << *it << " ";
	cout << endl;
}

void test01()
{
    
    
	deque<int> d;
	d.push_back(10);
	d.push_back(20);
	d.push_front(100);
	d.push_front(200);

	printDeque(d);  // 200 100 10 20
	// 排序算法:默认从小到大排
	sort(d.begin(), d.end());
	printDeque(d);  // 10 20 100 200
}

Notice:

  • Because vector is more like an array, it has one more capacity attribute than deque

4.4 stack and queue containers

4.4.1 stack container

  • Concept: stack is a first-in-last-out (FILO) data structure

insert image description here

  • Only the top element in the stack can be used by the outside world, so the stack does not allow traversal behavior

  • Entering data in the stack is called - stacking push

  • Popping data from the stack is called - popping the stack pop


Common interface:

Constructor:

  • stack<T> stk;// stack is implemented using a template class, and the default construction form of the stack object
  • stack(const stack &stk);// copy constructor

Assignment operation:

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

Data access:

  • push(elem);// Add an element to the top of the stack
  • pop();// remove the first element from the top of the stack
  • top();<span> </span>// Return the top element of the stack

Size operation:

  • empty();// Check if the stack is empty
  • size();// Return the size of the stack
#include <stack>

// 栈容器常用接口
void test01()
{
    
    
	// 创建栈容器,栈容器必须符合先进后出
	stack<int> s;

	// 向栈中添加元素,叫做压栈、入栈
	s.push(10);
	s.push(20);
	s.push(30);

	while (!s.empty()) {
    
    
		// 输出栈顶元素
		cout << "栈顶元素为: " << s.top() << endl;
		// 弹出栈顶元素
		s.pop();
	}

	cout << "栈的大小为:" << s.size() << endl;  // 0
}

4.4.2 queue container

Concept: Queue is a first-in-first-out (First In First Out, FIFO) data structure, which has two exits

insert image description here

  • The queue container allows adding elements from one end and removing elements from the other end

  • Only the head and tail of the queue can be used by the outside world, so the queue does not allow traversal behavior

  • Entering data in the queue is called enqueuing push

  • The data in the queue is called - dequeue pop


Common interface:

Constructor:

  • queue<T> que;// queue is implemented by template class, the default construction form of queue object
  • queue(const queue &que);// copy constructor

Assignment operation:

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

Data access:

  • push(elem);// Add elements to the end of the queue
  • pop();// remove the first element from the head of the queue
  • back();// return the last element
  • front();<span> </span>// return the first element

Size operation:

  • empty();// Check if the stack is empty
  • size();// Return the size of the stack
#include <queue>
#include <string>


class Person
{
    
    
public:
	Person(string name, int age)
	{
    
    
		this->m_Name = name;
		this->m_Age = age;
	}

	string m_Name;
	int m_Age;
};

void test01()
{
    
    
	// 创建队列
	queue<Person> q;
	// 准备数据
	Person p1("唐僧", 30);
	Person p2("孙悟空", 1000);
	Person p3("猪八戒", 900);
	Person p4("沙僧", 800);

	// 向队列中添加元素,入队操作
	q.push(p1);
	q.push(p2);
	q.push(p3);
	q.push(p4);

	// 队列不提供迭代器,更不支持随机访问
	while (!q.empty()) {
    
    
		// 输出队头元素
		cout << "队头元素-- 姓名: " << q.front().m_Name << " 年龄: "<< q.front().m_Age << endl;
		cout << "队尾元素-- 姓名: " << q.back().m_Name << " 年龄: " << q.back().m_Age << endl;
  
		cout << endl;
		// 弹出队头元素
		q.pop();
	}
	cout << "队列大小为:" << q.size() << endl;
}

4.5 list container

Function: chain storage of data

A linked list (list) is a discontinuous storage structure on a physical storage unit, and the logical order of data elements is realized through pointer links in the linked list

The composition of the linked list: the linked list is composed of a series of nodes

The composition of the node: one is a data field that stores data elements, and the other is a pointer field that stores the address of the next node

The linked list in STL is a doubly circular linked list

insert image description here

Since the storage method of the linked list is not a continuous memory space, the iterator in the linked list list only supports forward and backward movement , which is a bidirectional iterator

Advantages of lists:

  • Using dynamic storage allocation, will not cause memory waste and overflow
  • The linked list is very convenient to perform insertion and deletion operations, just modify the pointer without moving a large number of elements

Disadvantages of lists:

  • The linked list is flexible, but the space (pointer field) and time (traversal) are extra expensive

The list has an important property, the insertion operation and the deletion operation will not invalidate the original list iterator, which is not true in the vector

Summary: List and vector are the two most commonly used containers in STL, each with its own advantages and disadvantages


4.5.1 The list constructor

Function prototype:

  • list<T> lst;// list is implemented using a template class, the default construction form of the object
  • list(beg,end);// The constructor copies the elements in the range [beg, end) to itself
  • list(n,elem);// The constructor copies n elems to itself
  • list(const list &lst);// copy constructor
#include <list>

void printList(const list<int>& L) {
    
    
	for (list<int>::const_iterator it = L.begin(); it != L.end(); it++)	cout << *it << " ";
	cout << endl;
}

void test01()
{
    
    
	list<int> L1;  // ,默认构造

	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(30);
	L1.push_back(40);

	printList(L1);  // 10 20 30 40

	// 按照区间方式构造
	list<int> L2(L1.begin(),L1.end());
	printList(L2);  // 10 20 30 40

	// 拷贝构造
	list<int> L3(L2);
	printList(L3);  // 10 20 30 40

	// n 个 elem 拷贝方式
	list<int>L4(10, 100);
	printList(L4);  // 100 100 100 100 100 100 100 100 100 100
}

4.5.2 List assignment and exchange

Function prototype:

  • assign(beg, end);// Assign the copy of the data in the interval [beg, end) to itself
  • assign(n, elem);// Copy and assign n elems to itself
  • list& operator=(const list &lst);// overloaded equals operator
  • swap(lst);// swap lst with its own elements
#include <list>

void printList(const list<int>& L)
{
    
    
	for (list<int>::const_iterator it = L.begin(); it != L.end(); it++) cout << *it << " ";
	cout << endl;
}

// 赋值和交换
void test01()
{
    
    
	list<int> L1;
	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(30);
	L1.push_back(40);
	printList(L1);

	// 赋值
	list<int> L2;
	L2 = L1;
	printList(L2);

	list<int> L3;
	L3.assign(L2.begin(), L2.end());
	printList(L3);

	list<int> L4;
	L4.assign(10, 100);
	printList(L4);
}

// 交换
void test02()
{
    
    
	list<int> L1;
	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(30);
	L1.push_back(40);

	list<int> L2;
	L2.assign(10, 100);

	L1.swap(L2);
	printList(L1);
	printList(L2);
}

4.5.3 List Size Operations

Function prototype:

  • size();// returns the number of elements in the container
  • empty();// Check if the container is empty
  • 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
  • resize(num, elem);// Re-designate the length of the container as num. If the container becomes longer, fill the new position with elem value. If the container gets shorter, elements at the end that exceed the length of the container are removed.
#include <list>

void printList(const list<int>& L)
{
    
    
	for (list<int>::const_iterator it = L.begin(); it != L.end(); it++)	cout << *it << " ";
	cout << endl;
}

// 大小操作
void test01()
{
    
    
	list<int> L1;
	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(30);
	L1.push_back(40);

	if (L1.empty())	cout << "L1为空" << endl;
	else
	{
    
    
		cout << "L1不为空" << endl;
		cout << "L1的大小为: " << L1.size() << endl;
	}

	// 重新指定大小
	L1.resize(10);
	printList(L1);

	L1.resize(2);
	printList(L1);
}

4.5.4 list insertion and deletion

Function prototype:

  • push_back(elem);// add an element to the end of the container
  • pop_back();// delete the last element in the container
  • push_front(elem);// Insert an element at the beginning of the container
  • pop_front();// remove the first element from the beginning of the container
  • insert(pos,elem);// Insert a copy of the elem element at position pos, and return the position of the new data
  • insert(pos,n,elem);// insert n elem data at position pos, no return value
  • insert(pos,beg,end);// Insert the data in [beg,end) interval at pos position, no return value
  • clear();// remove all data from the container
  • erase(beg,end);// Delete the data in the interval [beg, end) and return the position of the next data
  • erase(pos);// Delete the data at position pos and return the position of the next data
  • remove(elem);// Delete all elements in the container that match the value of elem
#include <list>

void printList(const list<int>& L)
{
    
    
	for (list<int>::const_iterator it = L.begin(); it != L.end(); it++)	cout << *it << " ";
	cout << endl;
}

// 插入和删除
void test01()
{
    
    
	list<int> L;
	// 尾插
	L.push_back(10);
	L.push_back(20);
	L.push_back(30);
	// 头插
	L.push_front(100);
	L.push_front(200);
	L.push_front(300);

	printList(L);

	// 尾删
	L.pop_back();
	printList(L);

	// 头删
	L.pop_front();
	printList(L);

	// 插入
	list<int>::iterator it = L.begin();
	L.insert(++it, 1000);
	printList(L);

	// 删除
	it = L.begin();
	L.erase(++it);
	printList(L);

	// 移除
	L.push_back(10000);
	L.push_back(10000);
	L.push_back(10000);
	printList(L);
	L.remove(10000);
	printList(L);
  
    	// 清空
	L.clear();
	printList(L);
}

Note: list has a remove method to delete the specified element


4.5.5 list data access

Function prototype:

  • front();// return the first element
  • back();// return the last element
#include <list>

// 数据存取
void test01()
{
    
    
	list<int> L1;
	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(30);
	L1.push_back(40);

	// cout << L1.at(0) << endl;  // 错误,不支持 at 访问数据
	// cout << L1[0] << endl;  // 错误,不支持 [] 方式访问数据
	cout << "第一个元素为: " << L1.front() << endl;
	cout << "最后一个元素为: " << L1.back() << endl;

	// list 容器的迭代器是双向迭代器,不支持随机访问
	list<int>::iterator it = L1.begin();
	// it = it + 1;  // 错误,不可以跳跃访问,即使是 +1
}

Summary: The list container cannot access data through [] or at


4.5.6 List inversion and sorting

Function prototype:

  • reverse();// reverse linked list
  • sort();// list sort
void printList(const list<int>& L)
{
    
    
	for (list<int>::const_iterator it = L.begin(); it != L.end(); it++)	cout << *it << " ";
	cout << endl;
}

bool myCompare(int val1 , int val2)	{
    
     return val1 > val2; }

// 反转和排序
void test01()
{
    
    
	list<int> L;
	L.push_back(90);
	L.push_back(30);
	L.push_back(20);
	L.push_back(70);
	printList(L);  // 90 30 20 70

	// 反转容器的元素
	L.reverse();
	printList(L);  // 70 20 30 90

	// 排序
	L.sort();  // 默认的排序规则,从小到大
	printList(L);  // 20 30 70 90

	L.sort(myCompare);  // 指定规则,从大到小
	printList(L);  // 90 70 30 20
}

4.6 set/multiset containers

Introduction to set:

  • All elements are automatically sorted on insertion

The essence of set:

  • set/multiset is an associative container, and the underlying structure is implemented with a binary tree

The difference between set and multiset:

  • set does not allow duplicate elements in the container
  • multiset allows duplicate elements in the container

4.6.1 Set construction and assignment

structure:

  • set<T> st;// default constructor
  • set(const set &st);// copy constructor

assignment:

  • set& operator=(const set &st);// overloaded equals operator
#include <set>

void printSet(set<int> & s)
{
    
    
	for (set<int>::iterator it = s.begin(); it != s.end(); it++)	cout << *it << " ";
	cout << endl;
}

// 构造和赋值
void test01()
{
    
    
	set<int> s1;

	s1.insert(10);
	s1.insert(30);
	s1.insert(20);
	s1.insert(40);
	printSet(s1);

	// 拷贝构造
	set<int>s2(s1);
	printSet(s2);

	// 赋值
	set<int>s3;
	s3 = s2;
	printSet(s3);
}

Summarize:

  • Use insert when inserting data into the set container
  • The data inserted into the set container will be sorted automatically

4.6.2 Set size and swapping

Function prototype:

  • size();// returns the number of elements in the container
  • empty();// Check if the container is empty
  • swap(st);// Swap two collection containers
#include <set>

void printSet(set<int> & s)
{
    
    
	for (set<int>::iterator it = s.begin(); it != s.end(); it++)	cout << *it << " ";
	cout << endl;
}

// 大小
void test01()
{
    
    
	set<int> s1;

	s1.insert(10);
	s1.insert(30);
	s1.insert(20);
	s1.insert(40);

	if (s1.empty())	cout << "s1 为空" << endl;
	else
	{
    
    
		cout << "s1 不为空" << endl;
		cout << "s1 的大小为: " << s1.size() << endl;
	}
}

// 交换
void test02()
{
    
    
	set<int> s1;

	s1.insert(10);
	s1.insert(30);
	s1.insert(20);
	s1.insert(40);

	set<int> s2;

	s2.insert(100);
	s2.insert(300);
	s2.insert(200);
	s2.insert(400);

	cout << "交换前" << endl;
	printSet(s1);
	printSet(s2);
	cout << endl;

	cout << "交换后" << endl;
	s1.swap(s2);
	printSet(s1);
	printSet(s2);
}

4.6.3 set insertion and deletion

Function prototype:

  • insert(elem);// Insert elements into the container
  • clear();// clear all elements
  • erase(pos);// Delete the element pointed by the pos iterator and return the iterator of the next element
  • erase(beg, end);// Delete all elements in the interval [beg, end), and return an iterator to the next element
  • erase(elem);// Delete the element whose value is elem in the container
#include <set>

void printSet(set<int> & s)
{
    
    
	for (set<int>::iterator it = s.begin(); it != s.end(); it++)	cout << *it << " ";
	cout << endl;
}

// 插入和删除
void test01()
{
    
    
	set<int> s1;
	// 插入
	s1.insert(10);
	s1.insert(30);
	s1.insert(20);
	s1.insert(40);
	printSet(s1);

	// 删除
	s1.erase(s1.begin());
	printSet(s1);

	s1.erase(30);
	printSet(s1);

	// 清空
	// s1.erase(s1.begin(), s1.end());
	s1.clear();
	printSet(s1);
}

4.6.4 set lookup and statistics

Function prototype:

  • find(key);// Find whether the key exists, if it exists, return the iterator of the element of the key; if it does not exist, return set.end();
  • count(key);// Count the number of elements in the key
#include <set>

// 查找和统计
void test01()
{
    
    
	set<int> s1;
	// 插入
	s1.insert(10);
	s1.insert(30);
	s1.insert(20);
	s1.insert(40);

	// 查找
	set<int>::iterator pos = s1.find(30);  // 注意:因为返回的是元素的迭代器,取值的话要用 * 

	if (pos != s1.end())	cout << "找到了元素 : " << *pos << endl;
	else	cout << "未找到元素" << endl;

	// 统计
	int num = s1.count(30);
	cout << "num = " << num << endl;
}

4.6.5 Difference between set and multiset

the difference:

  • set cannot insert duplicate data, but multiset can
  • When set inserts data, it will return the insertion result, indicating whether the insertion is successful
  • multiset does not detect data, so duplicates can be inserted
#include <set>

// set 和 multiset 区别
void test01()
{
    
    
	set<int> s;
	pair<set<int>::iterator, bool>  ret = s.insert(10);  // 第一次插入成功!

	if (ret.second)	cout << "第一次插入成功!" << endl;
	else	cout << "第一次插入失败!" << endl;

	ret = s.insert(10);  // 第二次插入失败!
	if (ret.second)	cout << "第二次插入成功!" << endl;
	else	cout << "第二次插入失败!" << endl;

	// multiset
	multiset<int> ms;
	ms.insert(10);
	ms.insert(10);

	for (multiset<int>::iterator it = ms.begin(); it != ms.end(); it++)	cout << *it << " ";  // 10 10
	cout << endl;
}

4.6.6 pair pair creation

Function:

  • Data that appears in pairs, using pairs can return two data

Two ways to create:

  • pair<type, type> p(value1, value2);
  • pair<type, type> p = make_pair(value1, value2);
#include <string>

// 对组创建
void test01()
{
    
    
	pair<string, int> p("Tom", 20);
	cout << "姓名: " <<  p.first << " 年龄: " << p.second << endl;

	pair<string, int> p2 = make_pair("Jerry", 10);
	cout << "姓名: " << p2.first << " 年龄: " << p2.second << endl;
}

4.6.7 Set container sorting

Main technical points:

  • Using functors, you can change the sorting rules

Before storing the data, determine the collation rules!

Example 1: set stores built-in data types

#include <set>

class MyCompare 
{
    
    
public:
	bool operator()(int v1, int v2) {
    
     return v1 > v2; }
};

void test01() 
{
    
      
	set<int> s1;
	s1.insert(10);
	s1.insert(40);
	s1.insert(20);
	s1.insert(30);
	s1.insert(50);

	// 默认从小到大
	for (set<int>::iterator it = s1.begin(); it != s1.end(); it++)	cout << *it << " ";
	cout << endl;

	// 指定排序规则,从大到小
	set<int, MyCompare> s2;  // 在还未存放数据之前,确定好排序规则!
	s2.insert(10);
	s2.insert(40);
	s2.insert(20);
	s2.insert(30);
	s2.insert(50);

	for (set<int, MyCompare>::iterator it = s2.begin(); it != s2.end(); it++)	cout << *it << " ";
	cout << endl;
}

Example 2: set stores custom data types

#include <set>
#include <string>

class Person
{
    
    
public:
	Person(string name, int age)
	{
    
    
		this->m_Name = name;
		this->m_Age = age;
	}

	string m_Name;
	int m_Age;

};

class comparePerson
{
    
    
public:
	// 按照年龄进行排序,降序
	bool operator()(const Person& p1, const Person &p2) {
    
     return p1.m_Age > p2.m_Age; }
};

void test01()
{
    
    
	set<Person, comparePerson> s;

	Person p1("刘备", 23);
	Person p2("关羽", 27);
	Person p3("张飞", 25);
	Person p4("赵云", 21);

	s.insert(p1);
	s.insert(p2);
	s.insert(p3);
	s.insert(p4);

	for (set<Person, comparePerson>::iterator it = s.begin(); it != s.end(); it++)
	{
    
    
		cout << "姓名: " << it->m_Name << " 年龄: " << it->m_Age << endl;
	}
}

Summary: For custom data types, set must specify a collation to insert data


4.7 map/multimap container

Introduction to map:

  • All elements in the map are pairs
  • The first element in the pair is key (key value), which acts as an index, and the second element is value (real value)
  • All elements are automatically sorted according to the key value of the element

The essence of map:

  • map/multimap is an associative container , and the underlying structure is implemented with a binary tree

Advantages of maps:

  • You can quickly find the value value based on the key value

The difference between map and multimap:

  • map does not allow duplicate key value elements in the container
  • multimap allows duplicate key value elements in the container

4.7.1 map construction and assignment

structure:

  • map<T1, T2> mp;// map default constructor
  • map(const map &mp);// copy constructor

assignment:

  • map& operator=(const map &mp);// overloaded equals operator
#include <map>

void printMap(map<int,int>&m)
{
    
    
	for (map<int, int>::iterator it = m.begin(); it != m.end(); it++)	cout << "key = " << it->first << " value = " << it->second << endl;
	cout << endl;
}

void test01()
{
    
    
	map<int, int> m;  // 默认构造
	m.insert(pair<int, int>(1, 10));
	m.insert(pair<int, int>(2, 20));
	m.insert(pair<int, int>(3, 30));
	printMap(m);

	map<int, int> m2(m);  // 拷贝构造
	printMap(m2);

	map<int, int> m3;
	m3 = m2;  // 赋值
	printMap(m3);
}

Summary: All elements in the map appear in pairs, and pairs are used when inserting data


4.7.2 Map size and swapping

Function prototype:

  • size();// returns the number of elements in the container
  • empty();// Check if the container is empty
  • swap(st);// Swap two collection containers
#include <map>

void printMap(map<int, int>& m)
{
    
    
	for (map<int, int>::iterator it = m.begin(); it != m.end(); it++)	cout << "key = " << it->first << " value = " << it->second << endl;
	cout << endl;
}

void test01()
{
    
    
	map<int, int> m;
	m.insert(pair<int, int>(1, 10));
	m.insert(pair<int, int>(2, 20));
	m.insert(pair<int, int>(3, 30));

	if (m.empty())	cout << "m 为空" << endl;
	else
	{
    
    
		cout << "m 不为空" << endl;
		cout << "m 的大小为: " << m.size() << endl;
	}
}

// 交换
void test02()
{
    
    
	map<int, int> m;
	m.insert(pair<int, int>(1, 10));
	m.insert(pair<int, int>(2, 20));
	m.insert(pair<int, int>(3, 30));

	map<int, int> m2;
	m2.insert(pair<int, int>(4, 100));
	m2.insert(pair<int, int>(5, 200));
	m2.insert(pair<int, int>(6, 300));

	cout << "交换前" << endl;
	printMap(m);
	printMap(m2);

	cout << "交换后" << endl;
	m.swap(m2);
	printMap(m);
	printMap(m2);
}

4.7.3 Map insertion and deletion

Function prototype:

  • insert(elem);​​​​ // Insert an element into the container
  • clear();// clear all elements
  • erase(pos);// Delete the element pointed by the pos iterator and return the iterator of the next element
  • erase(beg, end);// Delete all elements in the interval [beg, end), and return an iterator to the next element
  • erase(key);// Delete the element whose value is key in the container
#include <map>

void printMap(map<int,int>&m)
{
    
    
	for (map<int, int>::iterator it = m.begin(); it != m.end(); it++)	cout << "key = " << it->first << " value = " << it->second << endl;
	cout << endl;
}

void test01()
{
    
    
	// 插入
	map<int, int> m;
	// 第一种插入方式
	m.insert(pair<int, int>(1, 10));
	// 第二种插入方式
	m.insert(make_pair(2, 20));
	// 第三种插入方式
	m.insert(map<int, int>::value_type(3, 30));
	// 第四种插入方式
	m[4] = 40; 
	printMap(m);

	// 删除
	m.erase(m.begin());
	printMap(m);

	m.erase(3);
	printMap(m);

	// 清空
	m.erase(m.begin(),m.end());
	m.clear();
	printMap(m);
}

4.7.4 Map search and statistics

Function prototype:

  • find(key); ​ // Find whether the key exists, if it exists, return the iterator of the element of the key; if it does not exist, return set.end();
  • count(key); ​ // Count the number of elements in the key
#include <map>

// 查找和统计
void test01()
{
    
    
	map<int, int>m; 
	m.insert(pair<int, int>(1, 10));
	m.insert(pair<int, int>(2, 20));
	m.insert(pair<int, int>(3, 30));

	// 查找
	map<int, int>::iterator pos = m.find(3);

	if (pos != m.end())	cout << "找到了元素 key = " << (*pos).first << " value = " << (*pos).second << endl;
	else	cout << "未找到元素" << endl;

	// 统计
	int num = m.count(3);
	cout << "num = " << num << endl;
}

4.7.5 Map container sorting

Main technical points:

  • Using functors, you can change the sorting rules
#include <map>

class MyCompare {
    
    
public:
	bool operator()(int v1, int v2) {
    
     return v1 > v2; }
};

void test01() 
{
    
    
	// 利用仿函数实现从大到小排序
	map<int, int, MyCompare> m;

	m.insert(make_pair(1, 10));
	m.insert(make_pair(2, 20));
	m.insert(make_pair(3, 30));
	m.insert(make_pair(4, 40));
	m.insert(make_pair(5, 50));

	for (map<int, int, MyCompare>::iterator it = m.begin(); it != m.end(); it++)	cout << "key:" << it->first << " value:" << it->second << endl;
}

Summary: For custom data types, map must specify collation, same as set container


5 function objects

concept:

  • A class that overloads the function call operator, its object is often called a function object
  • When the function object uses overloaded (), it behaves like a function call, also called a functor

Nature:

A function object (functor) is a class, not a function

Features:

  • When a function object is used, it can be called like a normal function, it can have parameters, and it can have a return value
  • Function objects go beyond the concept of ordinary functions, and function objects can have their own state
  • Function objects can be passed as arguments
#include <string>

// 1、函数对象在使用时,可以像普通函数那样调用, 可以有参数,可以有返回值
class MyAdd
{
    
    
public:
	int operator()(int v1, int v2) {
    
     return v1 + v2; }
};

void test01()
{
    
    
	MyAdd myAdd;
	cout << myAdd(10, 10) << endl;  // 20
}

// 2、函数对象可以有自己的状态
class MyPrint
{
    
    
public:
	MyPrint() {
    
     count = 0; }

	void operator()(string test)
	{
    
    
		cout << test << endl;
		count++;  // 统计使用次数
	}

	int count;  // 内部自己的状态
};

void test02()
{
    
    
	MyPrint myPrint;
	myPrint("hello world");
	myPrint("hello world");
	myPrint("hello world");
	cout << "myPrint 调用次数为: " << myPrint.count << endl;
}

// 3、函数对象可以作为参数传递
void doPrint(MyPrint& mp , string test)	{
    
     mp(test); }

void test03()
{
    
    
	MyPrint myPrint;

	doPrint(myPrint, "Hello C++");
	cout << "myPrint 调用次数为: " << myPrint.count << endl;  // 1
}

5.1 Predicates

concept:

  • Functors that return bool type are called predicates
  • If operator() takes one argument, it is called a unary predicate
  • If operator() takes two arguments, it is called a binary predicate

Unary predicates:

#include <vector>
#include <algorithm>

// 二元谓词
class MyCompare
{
    
    
public:
	bool operator()(int num1, int num2)
	{
    
    
		return num1 > num2;
	}
};

void test01()
{
    
    
	vector<int> v;
	v.push_back(10);
	v.push_back(40);
	v.push_back(20);
	v.push_back(30);
	v.push_back(50);

	// 默认从小到大
	sort(v.begin(), v.end());
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
    
    
		cout << *it << " ";
	}
	cout << endl;
	cout << "----------------------------" << endl;

	// 使用函数对象改变算法策略,排序从大到小
	sort(v.begin(), v.end(), MyCompare());
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
    
    
		cout << *it << " ";
	}
	cout << endl;
}

Binary predicates:

#include <vector>
#include <algorithm>

// 二元谓词
class MyCompare
{
    
    
public:
	bool operator()(int num1, int num2)
	{
    
    
		return num1 > num2;
	}
};

void test01()
{
    
    
	vector<int> v;
	v.push_back(10);
	v.push_back(40);
	v.push_back(20);
	v.push_back(30);
	v.push_back(50);

	// 默认从小到大
	sort(v.begin(), v.end());
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
    
    
		cout << *it << " ";
	}
	cout << endl;
	cout << "----------------------------" << endl;

	// 使用函数对象改变算法策略,排序从大到小
	sort(v.begin(), v.end(), MyCompare());
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
    
    
		cout << *it << " ";
	}
	cout << endl;
}

5.2 Built-in function objects

concept:

  • STL has some built-in function objects

Classification:

  • arithmetic functor
  • relational functor
  • logical functor

usage:

  • The objects generated by these functors are used in exactly the same way as ordinary functions
  • To use the built-in function object, you need to import the header file#include<functional>

5.2.1 Arithmetic functors

Function description:

  • Realize the four operations
  • Among them, negate is a unary operation, and the others are binary operations

Functor prototype:

  • template<class T> T plus<T>// addition functor
  • template<class T> T minus<T>// Subtraction functor
  • template<class T> T multiplies<T>// multiply functor
  • template<class T> T divides<T>// division functor
  • template<class T> T modulus<T>// take mock function
  • template<class T> T negate<T>// Take the inverse functor
#include <functional>

// negate
void test01()
{
    
    
	negate<int> n;
	cout << n(50) << endl;  // -50
}

// plus
void test02()
{
    
    
	plus<int> p;
	cout << p(10, 20) << endl;  // 30
}

Summary: When using built-in function objects, you need to introduce header files#include <functional>


5.2.2 Relational functors

Function description:

  • Realize relational comparison

Functor prototype:

  • template<class T> bool equal_to<T>// is equal to
  • template<class T> bool not_equal_to<T>// not equal to
  • template<class T> bool greater<T>// more than the
  • template<class T> bool greater_equal<T>// greater or equal to
  • template<class T> bool less<T>// less than
  • template<class T> bool less_equal<T>// less than or equal to
#include <functional>
#include <vector>
#include <algorithm>

class MyCompare
{
    
    
public:
	bool operator()(int v1,int v2)	{
    
     return v1 > v2; }
};
void test01()
{
    
    
	vector<int> v;

	v.push_back(10);
	v.push_back(30);
	v.push_back(50);
	v.push_back(40);
	v.push_back(20);

	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)	cout << *it << " ";
	cout << endl;

	// 自己实现仿函数
	// sort(v.begin(), v.end(), MyCompare());
	// STL 内建仿函数  大于仿函数
	sort(v.begin(), v.end(), greater<int>());

	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)	cout << *it << " ";
	cout << endl;
}

Summary: The most commonly used relational functor is greater<> greater than


5.2.3 Logical functors

Function prototype:

  • template<class T> bool logical_and<T>// logical AND
  • template<class T> bool logical_or<T>// logical OR
  • template<class T> bool logical_not<T>// logical NOT
#include <vector>
#include <functional>
#include <algorithm>

void test01()
{
    
    
	vector<bool> v;
	v.push_back(true);
	v.push_back(false);
	v.push_back(true);
	v.push_back(false);

	for (vector<bool>::iterator it = v.begin();it!= v.end();it++)	cout << *it << " ";
	cout << endl;

	// 逻辑非  将 v 容器搬运到 v2 中,并执行逻辑非运算
	vector<bool> v2;
	v2.resize(v.size());
	transform(v.begin(), v.end(),  v2.begin(), logical_not<bool>());
	for (vector<bool>::iterator it = v2.begin(); it != v2.end(); it++)	cout << *it << " ";
	cout << endl;
}

6 STL Common Algorithms

Overview:

  • <algorithm> <functional> <numeric>The algorithm is mainly composed of header files
  • <algorithm>It is the largest of all STL header files, and its scope involves comparison, exchange, search, traversal operations, copying, modification, etc.
  • <numeric>Small in size, only includes a few template functions to perform simple mathematical operations on sequences
  • <functional>Some template classes are defined to declare function objects

6.1 Commonly used traversal algorithms

Algorithm introduction:

  • for_each// traverse the container
  • transform// move the container to another container

6.1.1 for_each

Function prototype:

  • for_each(iterator beg, iterator end, _func);

    // The traversal algorithm traverses the container elements

    // beg starts the iterator

    // end end iterator

    // _func function or function object

#include <algorithm>
#include <vector>

// 普通函数
void print01(int val)	{
    
     cout << val << " "; }

// 函数对象
class print02 
{
    
    
 public:
	void operator()(int val) {
    
     cout << val << " "; }
};

// for_each 算法基本用法
void test01()
{
    
    
	vector<int> v;
	for (int i = 0; i < 10; i++)	v.push_back(i);

	// 遍历算法
	for_each(v.begin(), v.end(), print01);
	cout << endl;

	for_each(v.begin(), v.end(), print02());
	cout << endl;
}

Note: for_each is the most commonly used traversal algorithm in actual development and needs to be mastered


6.1.2 transform

Function:

  • Move a container to another container

Function prototype:

  • transform(iterator beg1, iterator end1, iterator beg2, _func);

    // beg1 source container start iterator

    // end1 source container end iterator

    // beg2 target container start iterator

    // _func function or function object

#include<vector>
#include<algorithm>

// 常用遍历算法  搬运 transform
class TransForm
{
    
    
public:
	int operator()(int val)	{
    
     return val; }
};

class MyPrint
{
    
    
public:
	void operator()(int val) {
    
     cout << val << " "; }
};

void test01()
{
    
    
	vector<int> v;
	for (int i = 0; i < 10; i++)	v.push_back(i);

	vector<int> vTarget;  // 目标容器

	vTarget.resize(v.size());  // 目标容器需要提前开辟空间

	transform(v.begin(), v.end(), vTarget.begin(), TransForm());

	for_each(vTarget.begin(), vTarget.end(), MyPrint());  // 0 1 2 3 4 5 6 7 8 9
}

Note: The target container to be transported must open up space in advance, otherwise it cannot be transported normally


6.2 Common search algorithms

Algorithm introduction:

  • find// find element
  • find_if// find element by condition
  • adjacent_find// find adjacent duplicate elements
  • binary_search// binary search method
  • count// Count the number of elements
  • count_if// Count the number of elements by condition

6.2.1 find

Function:

  • Find the specified element, find the iterator that returns the specified element, if not found, return the end iterator end()

Function prototype:

  • find(iterator beg, iterator end, value);

    // Find the element by value, if found, return the iterator at the specified position, if not found, return the end iterator position

    // beg starts the iterator

    // end end iterator

    // The element that value is looking for

#include <algorithm>
#include <vector>
#include <string>

void test01()
{
    
    
	vector<int> v;
	for (int i = 0; i < 10; i++)	v.push_back(i + 1);

	// 查找容器中是否有 5 这个元素
	vector<int>::iterator it = find(v.begin(), v.end(), 5);
	if (it == v.end())	cout << "没有找到!" << endl;
	else	cout << "找到:" << *it << endl;
}

class Person {
    
    
public:
	Person(string name, int age) 
	{
    
    
		this->m_Name = name;
		this->m_Age = age;
	}
	// 重载 ==
	bool operator==(const Person& p) 
	{
    
    
		if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)	return true;
		return false;
	}

public:
	string m_Name;
	int m_Age;
};

void test02()
{
    
    
	vector<Person> v;

	// 创建数据
	Person p1("aaa", 10);
	Person p2("bbb", 20);
	Person p3("ccc", 30);
	Person p4("ddd", 40);

	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);

	vector<Person>::iterator it = find(v.begin(), v.end(), p2);
	if (it == v.end())	cout << "没有找到!" << endl;
	else	cout << "找到姓名:" << it->m_Name << " 年龄: " << it->m_Age << endl;
}

Summary: Use find to find the specified element in the container, and the return value is an iterator


6.2.2 find_if

Function:

  • find element by condition

Function prototype:

  • find_if(iterator beg, iterator end, _Pred);

    // Find the element by value, if found, return the iterator at the specified position, if not found, return the end iterator position

    // beg starts the iterator

    // end end iterator

    // _Pred function or predicate (functor returning bool type)

#include <algorithm>
#include <vector>
#include <string>

// 内置数据类型
class GreaterFive
{
    
    
public:
	bool operator()(int val) {
    
     return val > 5; }
};

void test01()
{
    
    
	vector<int> v;
	for (int i = 0; i < 10; i++)	v.push_back(i + 1);

	vector<int>::iterator it = find_if(v.begin(), v.end(), GreaterFive());
	if (it == v.end())	cout << "没有找到!" << endl;
	else	cout << "找到:" << *it << endl;
}

// 自定义数据类型
class Person {
    
    
public:
	Person(string name, int age)
	{
    
    
		this->m_Name = name;
		this->m_Age = age;
	}
public:
	string m_Name;
	int m_Age;
};

class Greater20
{
    
    
public:
	bool operator()(Person& p) {
    
     return p.m_Age > 20; }
};

void test02()
{
    
    
	vector<Person> v;

	// 创建数据
	Person p1("aaa", 10);
	Person p2("bbb", 20);
	Person p3("ccc", 30);
	Person p4("ddd", 40);

	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);

	vector<Person>::iterator it = find_if(v.begin(), v.end(), Greater20());
	if (it == v.end())	cout << "没有找到!" << endl;
	else	cout << "找到姓名:" << it->m_Name << " 年龄: " << it->m_Age << endl;  // 找到姓名:ccc 年龄: 30
}

Summary: find_if finds by condition to make the search more flexible, and the functor provided can change different strategies


6.2.3 adjacent_find

Function:

  • Find adjacent duplicate elements

Function prototype:

  • adjacent_find(iterator beg, iterator end);

    // Find adjacent repeated elements and return an iterator to the first position of the adjacent element

    // beg starts the iterator

    // end end iterator

#include <algorithm>
#include <vector>

void test01()
{
    
    
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(5);
	v.push_back(2);
	v.push_back(4);
	v.push_back(4);
	v.push_back(3);

	// 查找相邻重复元素
	vector<int>::iterator it = adjacent_find(v.begin(), v.end());
	if (it == v.end())	cout << "找不到!" << endl;
	else cout << "找到相邻重复元素为:" << *it << endl;
}

Summary: If you find adjacent repeated elements in the interview questions, remember to use the adjacent_find algorithm in STL


6.2.4 binary_search

Binary search function:

  • Find if the specified element exists

Function prototype:

  • bool binary_search(iterator beg, iterator end, value);

    // Find the specified element, return true if found, otherwise false

    // note: not available in unordered sequences

    // beg starts the iterator

    // end end iterator

    // The element that value is looking for

#include <algorithm>
#include <vector>

void test01()
{
    
    
	vector<int>v;

	for (int i = 0; i < 10; i++)	v.push_back(i);
	// 二分查找
	bool ret = binary_search(v.begin(), v.end(), 2);
	if (ret)	cout << "找到了" << endl;
	else	cout << "未找到" << endl;
}

Summary: The binary search method is very efficient. It is worth noting that the elements in the searched container must have an ordered sequence


6.2.5 count

Function:

  • Number of statistical elements

Function prototype:

  • count(iterator beg, iterator end, value);

    // Count the number of occurrences of the element

    // beg starts the iterator

    // end end iterator

    // elements of value statistics

#include <algorithm>
#include <vector>

// 内置数据类型
void test01()
{
    
    
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(4);
	v.push_back(5);
	v.push_back(3);
	v.push_back(4);
	v.push_back(4);

	int num = count(v.begin(), v.end(), 4);

	cout << "4 的个数为: " << num << endl;
}

// 自定义数据类型
class Person
{
    
    
public:
	Person(string name, int age)
	{
    
    
		this->m_Name = name;
		this->m_Age = age;
	}
	bool operator==(const Person & p)
	{
    
    
		if (this->m_Age == p.m_Age)	return true;
		else	return false;
	}
	string m_Name;
	int m_Age;
};

void test02()
{
    
    
	vector<Person> v;

	Person p1("刘备", 35);
	Person p2("关羽", 35);
	Person p3("张飞", 35);
	Person p4("赵云", 30);
	Person p5("曹操", 25);

	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	v.push_back(p5);
  
	Person p("诸葛亮", 35);

	int num = count(v.begin(), v.end(), p);
	// 注意重载运算符里的逻辑
	cout << "num = " << num << endl;  // num = 3
}

Summary: When counting custom data types, you need to cooperate with overloading**operator==**


6.2.6 count_if

Function:

  • Count the number of elements by condition

Function prototype:

  • count_if(iterator beg, iterator end, _Pred);

    // Count the number of occurrences of elements by condition

    // beg starts the iterator

    // end end iterator

    // _Pred predicate

#include <algorithm>
#include <vector>

class Greater4
{
    
    
public:
	bool operator()(int val) {
    
     return val >= 4; }
};

// 内置数据类型
void test01()
{
    
    
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(4);
	v.push_back(5);
	v.push_back(3);
	v.push_back(4);
	v.push_back(4);

	int num = count_if(v.begin(), v.end(), Greater4());

	cout << "大于4的个数为: " << num << endl;
}

// 自定义数据类型
class Person
{
    
    
public:
	Person(string name, int age)
	{
    
    
		this->m_Name = name;
		this->m_Age = age;
	}

	string m_Name;
	int m_Age;
};

class AgeLess35
{
    
    
public:
	bool operator()(const Person &p) {
    
     return p.m_Age < 35; }
};

void test02()
{
    
    
	vector<Person> v;

	Person p1("刘备", 35);
	Person p2("关羽", 35);
	Person p3("张飞", 35);
	Person p4("赵云", 30);
	Person p5("曹操", 25);

	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	v.push_back(p5);

	int num = count_if(v.begin(), v.end(), AgeLess35());
	cout << "小于35岁的个数:" << num << endl;
}

Summary: count by value and count_if by condition


6.3 Common sorting algorithms

Algorithm introduction:

  • sort// Sort the elements in the container
  • random_shuffle// Shuffle, randomly adjust the order of elements in the specified range
  • merge// The container elements are merged and stored in another container
  • reverse// Reverse the specified range of elements

6.3.1 sort

Function:

  • Sort the elements in the container

Function prototype:

  • sort(iterator beg, iterator end, _Pred);

    // Find the element by value, if found, return the iterator at the specified position, if not found, return the end iterator position

    // beg starts the iterator

    // end end iterator

    // _Pred predicate

#include <algorithm>
#include <vector>

void myPrint(int val) {
    
     cout << val << " ";}

void test01()
{
    
    
	vector<int> v;
	v.push_back(10);
	v.push_back(30);
	v.push_back(50);
	v.push_back(20);
	v.push_back(40);

	// sort 默认从小到大排序
	sort(v.begin(), v.end());
	for_each(v.begin(), v.end(), myPrint);
	cout << endl;

	// 从大到小排序
	sort(v.begin(), v.end(), greater<int>());
	for_each(v.begin(), v.end(), myPrint);
	cout << endl;
}

Summary: sort is one of the most commonly used algorithms in development and needs to be mastered


6.3.2 random_shuffle

Function:

  • Shuffle the cards, randomly adjust the order of the elements in the specified range

Function prototype:

  • random_shuffle(iterator beg, iterator end);

    // Randomly adjust the order of elements in the specified range

    // beg starts the iterator

    // end end iterator

#include <algorithm>
#include <vector>
#include <ctime>

class myPrint
{
    
    
public:
	void operator()(int val) {
    
     cout << val << " "; }
};

void test01()
{
    
    
	srand((unsigned int)time(NULL));  // 记得加随机数种子
	vector<int> v;
	for(int i = 0 ; i < 10;i++)	v.push_back(i);

	for_each(v.begin(), v.end(), myPrint());
	cout << endl;

	// 打乱顺序
	random_shuffle(v.begin(), v.end());
	for_each(v.begin(), v.end(), myPrint());
	cout << endl;
}

Summary: The random_shuffle shuffling algorithm is more practical, remember to add random number seeds when using it


6.3.3 merge

Function:

  • The two container elements are merged and stored in another container

Function prototype:

  • merge(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);

    // The container elements are merged and stored in another container

    // Note: the two containers must be in order

    // beg1 container 1 start iterator

    // end1 container 1 end iterator

    // beg2 container 2 start iterator

    // end2 container 2 end iterator

    // dest destination container start iterator

#include <algorithm>
#include <vector>

class myPrint
{
    
    
public:
	void operator()(int val) {
    
     cout << val << " "; }
};

void test01()
{
    
    
	vector<int> v1;
	vector<int> v2;
	for (int i = 0; i < 10 ; i++) 
    	{
    
    
		v1.push_back(i);
		v2.push_back(i + 1);
	}

	vector<int> vtarget;
	// 目标容器需要提前开辟空间
	vtarget.resize(v1.size() + v2.size());
	// 合并  需要两个有序序列
	merge(v1.begin(), v1.end(), v2.begin(), v2.end(), vtarget.begin());
	for_each(vtarget.begin(), vtarget.end(), myPrint());
	cout << endl;
}

Summary: The ordered sequence of the two containers merged by merge


6.3.4 reverse

Function:

  • Reverse the elements in the container

Function prototype:

  • reverse(iterator beg, iterator end);

    // Reverse the specified range of elements

    // beg starts the iterator

    // end end iterator

#include <algorithm>
#include <vector>

class myPrint
{
    
    
public:
	void operator()(int val) {
    
     cout << val << " "; }
};

void test01()
{
    
    
	vector<int> v;
	v.push_back(10);
	v.push_back(30);
	v.push_back(50);
	v.push_back(20);
	v.push_back(40);

	cout << "反转前: " << endl;
	for_each(v.begin(), v.end(), myPrint());
	cout << endl;

	reverse(v.begin(), v.end());

	cout << "反转后: " << endl;

	for_each(v.begin(), v.end(), myPrint());
	cout << endl;
}

Summary: reverse reverses the elements in the interval, interview questions may involve


6.4 Common Copy and Replace Algorithms

Algorithm introduction:

  • copy// Copy the elements of the specified range in the container to another container
  • replace// Modify the old elements in the specified range in the container to new elements
  • replace_if<span> </span>// Elements in the specified range in the container that meet the conditions are replaced with new elements
  • swap// Swap the elements of the two containers

6.4.1 copy

Function:

  • Copies the specified range of elements in a container to another container

Function prototype:

  • copy(iterator beg, iterator end, iterator dest);

    // Find the element by value, if found, return the iterator at the specified position, if not found, return the end iterator position

    // beg starts the iterator

    // end end iterator

    // dest target starting iterator

#include <algorithm>
#include <vector>

class myPrint
{
    
    
public:
	void operator()(int val) {
    
     cout << val << " "; }
};

void test01()
{
    
    
	vector<int> v1;
	for (int i = 0; i < 10; i++)	v1.push_back(i + 1);

	vector<int> v2;
	v2.resize(v1.size());
	copy(v1.begin(), v1.end(), v2.begin());

	for_each(v2.begin(), v2.end(), myPrint());
	cout << endl;
}

Summary: When copying using the copy algorithm, the target container remembers to open up space in advance


6.4.2 replace

Function:

  • Modify the old elements in the specified range in the container to new elements

Function prototype:

  • replace(iterator beg, iterator end, oldvalue, newvalue);

    // Replace the old elements in the interval with new elements

    // beg starts the iterator

    // end end iterator

    // oldvalue old element

    // newvalue new element

#include <algorithm>
#include <vector>

class myPrint
{
    
    
public:
	void operator()(int val) {
    
     cout << val << " "; }
};

void test01()
{
    
    
	vector<int> v;
	v.push_back(20);
	v.push_back(30);
	v.push_back(20);
	v.push_back(40);
	v.push_back(50);
	v.push_back(10);
	v.push_back(20);

	cout << "替换前:" << endl;
	for_each(v.begin(), v.end(), myPrint());
	cout << endl;

	// 将容器中的 20 替换成 2000
	replace(v.begin(), v.end(), 20,2000);

	cout << "替换后:" << endl;

	for_each(v.begin(), v.end(), myPrint());
	cout << endl;
}

Summary: replace will replace elements in the interval that meet the conditions


6.4.3 replace_if

Function:

  • Replace the elements in the interval that meet the conditions with the specified elements

Function prototype:

  • replace_if(iterator beg, iterator end, _pred, newvalue);

    // Replace elements according to the conditions, and replace the elements that meet the conditions with the specified elements

    // beg starts the iterator

    // end end iterator

    // _pred predicate

    // The new element replaced by newvalue

#include <algorithm>
#include <vector>

class myPrint
{
    
    
public:
	void operator()(int val) {
    
     cout << val << " "; }
};

class ReplaceGreater30
{
    
    
public:
	bool operator()(int val) {
    
     return val >= 30; }
};

void test01()
{
    
    
	vector<int> v;
	v.push_back(20);
	v.push_back(30);
	v.push_back(20);
	v.push_back(40);
	v.push_back(50);
	v.push_back(10);
	v.push_back(20);

	cout << "替换前:" << endl;
	for_each(v.begin(), v.end(), myPrint());
	cout << endl;

	// 将容器中大于等于的 30 替换成 3000
	cout << "替换后:" << endl;
	replace_if(v.begin(), v.end(), ReplaceGreater30(), 3000);
	for_each(v.begin(), v.end(), myPrint());
	cout << endl;
}

Summary: replace_if finds by condition, you can use the functor to flexibly filter the satisfied conditions


6.4.4 swap

Function:

  • Swaps the elements of two containers

Function prototype:

  • swap(container c1, container c2);

    // Swap the elements of the two containers

    // c1 container 1

    // c2 container 2

#include <algorithm>
#include <vector>

class myPrint
{
    
    
public:
	void operator()(int val) {
    
     cout << val << " "; }
};

void test01()
{
    
    
	vector<int> v1;
	vector<int> v2;

	for (int i = 0; i < 10; i++) {
    
    
		v1.push_back(i);
		v2.push_back(i+100);
	}

	cout << "交换前: " << endl;
	for_each(v1.begin(), v1.end(), myPrint());
	cout << endl;
	for_each(v2.begin(), v2.end(), myPrint());
	cout << endl;

	cout << "交换后: " << endl;
	swap(v1, v2);
	for_each(v1.begin(), v1.end(), myPrint());
	cout << endl;
	for_each(v2.begin(), v2.end(), myPrint());
	cout << endl;
}

Summary: When swapping containers, note that the swapped containers must be of the same type


6.5 Common Arithmetic Generation Algorithms

Notice:

  • The arithmetic generation algorithm is a small algorithm, and the header file included when using it is#include <numeric>

Algorithm introduction:

  • accumulate// Calculate the cumulative sum of the container elements
  • fill// add elements to the container

6.5.1 accumulate

Function:

  • Computes the cumulative sum of the container elements in the range

Function prototype:

  • accumulate(iterator beg, iterator end, value);

    // Calculate the cumulative sum of the container elements

    // beg starts the iterator

    // end end iterator

    // value start value

#include <numeric>
#include <vector>

void test01()
{
    
    
	vector<int> v;
	for (int i = 0; i <= 100; i++)	v.push_back(i);

	int total = accumulate(v.begin(), v.end(), 0);

	cout << "total = " << total << endl;
}

Summary: Note that the header file is numeric when using accumulate, this algorithm is very practical


6.5.2 fill

Function:

  • Fills the container with the specified elements

Function prototype:

  • fill(iterator beg, iterator end, value);

    // fill the container with elements

    // beg starts the iterator

    // end end iterator

    // value filled value

#include <numeric>
#include <vector>
#include <algorithm>

class myPrint
{
    
    
public:
	void operator()(int val) {
    
     cout << val << " "; }
};

void test01()
{
    
    
	vector<int> v;
	v.resize(10);
	// 填充
	fill(v.begin(), v.end(), 100);

	for_each(v.begin(), v.end(), myPrint());
	cout << endl;
}

Summary: Use fill to fill the elements in the container interval with the specified value


6.6 Common Set Algorithms

Algorithm introduction:

  • set_intersection// Find the intersection of two containers
  • set_union// Find the union of two containers
  • set_difference<span> </span>// Find the difference of two containers

6.6.1 set_intersection

Function:

  • find the intersection of two containers

Function prototype:

  • set_intersection(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);

    // Find the intersection of two sets

    // Note: the two collections must be ordered sequences

    // beg1 container 1 start iterator

    // end1 container 1 end iterator

    // beg2 container 2 start iterator

    // end2 container 2 end iterator

    // dest destination container start iterator

#include <vector>
#include <algorithm>

class myPrint
{
    
    
public:
	void operator()(int val) {
    
     cout << val << " "; }
};

void test01()
{
    
    
	vector<int> v1;
	vector<int> v2;
	for (int i = 0; i < 10; i++)
    	{
    
    
		v1.push_back(i);
		v2.push_back(i+5);
	}

	vector<int> vTarget;
	// 取两个里面较小的值给目标容器开辟空间
	vTarget.resize(min(v1.size(), v2.size()));

	// 返回目标容器的最后一个元素的迭代器地址
	vector<int>::iterator itEnd = set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());

	// 注意:结束迭代器参数是 set_intersection 返回值,只有存放了交集个数个数量,开辟了空间但是没有存值的空间用 0 填充
	for_each(vTarget.begin(), itEnd, myPrint());
	cout << endl;
}

Summarize:

  • the ordered sequence necessary for the intersection of two sets
  • The target container needs to take the small value from the two containers to open up space
  • The return value of set_intersection is the position of the last element in the intersection

6.6.2 set_union

Function:

  • find the union of two sets

Function prototype:

  • set_union(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);

    // find the union of two sets

    // Note: the two collections must be ordered sequences

    // beg1 container 1 start iterator

    // end1 container 1 end iterator

    // beg2 container 2 start iterator

    // end2 container 2 end iterator

    // dest destination container start iterator

#include <vector>
#include <algorithm>

class myPrint
{
    
    
public:
	void operator()(int val) {
    
     cout << val << " "; }
};

void test01()
{
    
    
	vector<int> v1;
	vector<int> v2;
	for (int i = 0; i < 10; i++)
	{
    
    
		v1.push_back(i);
		v2.push_back(i+5);
	}

	vector<int> vTarget;
	// 取两个容器的和给目标容器开辟空间
	vTarget.resize(v1.size() + v2.size());

	// 返回目标容器的最后一个元素的迭代器地址
	vector<int>::iterator itEnd = set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());

	for_each(vTarget.begin(), itEnd, myPrint());
	cout << endl;
}

Summarize:

  • The ordered sequence necessary to find the union of two sets
  • The target container needs to add two containers to open up space
  • The return value of set_union is the position of the last element in the union

6.6.3 set_difference

Function:

  • Find the difference of two sets

Function prototype:

  • set_difference(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);

    // Find the difference of two sets

    // Note: the two collections must be ordered sequences

    // beg1 container 1 start iterator

    // end1 container 1 end iterator

    // beg2 container 2 start iterator

    // end2 container 2 end iterator

    // dest destination container start iterator

#include <vector>
#include <algorithm>

class myPrint
{
    
    
public:
	void operator()(int val) {
    
     cout << val << " "; }
};

void test01()
{
    
    
	vector<int> v1;
	vector<int> v2;
	for (int i = 0; i < 10; i++)
	{
    
    
		v1.push_back(i);
		v2.push_back(i+5);
	}

	vector<int> vTarget;
	// 取两个里面较大的值给目标容器开辟空间
	vTarget.resize(max(v1.size() , v2.size()));

	// 返回目标容器的最后一个元素的迭代器地址
	cout << "v1与v2的差集为: " << endl;
	vector<int>::iterator itEnd = set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());
	for_each(vTarget.begin(), itEnd, myPrint());
	cout << endl;

	cout << "v2与v1的差集为: " << endl;
	itEnd = set_difference(v2.begin(), v2.end(), v1.begin(), v1.end(), vTarget.begin());
	for_each(vTarget.begin(), itEnd, myPrint());
	cout << endl;
}

Summarize:

  • The ordered sequence necessary for two sets to be subtracted
  • The target container needs to take the larger value from the two containers to open up space
  • The return value of set_difference is the position of the last element in the difference set

Guess you like

Origin blog.csdn.net/Coder_J7/article/details/130857081