• 初识Cpp之 九、模板和STL


    九、 模板和STL

    (1)模板

    ​ Cpp中另一种编程思想称为泛型编程,主要利用的技术是模板,Cpp提供两种模板机制函数模板类模板。模板就是建立通用的模具,大大提高程序的复用性。

    1、函数模板

    ​ 函数模板是通用的函数描述,函数模板使用泛型来定义函数,其中的泛型可用具体的类型(如double和int)替换。通过将类型作为参数传递给模板,可使编译器生成该类型的函数。这种方式也被称为通用编程。创建函数模板需要使用关键字==template和关键字typename==。

    ​ 函数模板的作用是,建立一个通用函数,其函数返回值类型和形参类型可以不具体指定,用一个虚拟的类型来表示。模板的创建语法:

    template <typename T>	//也可以使用class代替typename
    函数声明或定义
    
    • 1
    • 2

    解释:

    • template,声明创建模板;
    • typename,表示其后面的符号是一种数据类型,可以使用class来替换;、
    • T,通用的数据类型,名称可以替换,通常为大写字母。

    函数模板的使用有两种方式:

    • 自动类型推导;
    • 显示指定类型,语法:函数名<参数类型>(参数列表)

    示例:

    template <typename T>
    void Swap(T &a, T &b)		//定义了一个交换两对象值的函数
    {
        T temp = a;
        a = b;
        b = temp;
    }
    //函数模板的使用
    int num1 = 10;
    int num2 = 20;
    Swap(num1, num2);		//1.自动类型推导
    Swap<int>(num1, num2);	//2.显示指定类型
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    函数模板使用的注意事项

    • 自动类型推导,必须推导出一致的数据类型T,才可以使用;
    • 模板必须要确定出T的数据类型,才可以使用。

    示例:

    //函数模板完成数组的排序
    #include 
    using namespace std;
    template <class T>
    void Swap(T& a, T& b)		//模板交换函数
    {
    	T temp = a;
    	a = b;
    	b = temp;
    }
    template <class T>
    void SelectSort(T* arr, int len)		//选择排序
    {
    	for (int i = 0; i < len - 1; i++)
    	{
    		int max = i;
    		for (int j = i; j < len; j++)
    		{
    			if (arr[max] < arr[j])
    			{
    				max = j;
    			}
    		}
    		if (max != i)
    		{
    			Swap(arr[max], arr[i]);
    		}
    		
    	}
    }
    template <typename T>		//模板显示函数
    void showArray(T* arr, int len)
    {
    	for (int i = 0; i < len; i++)
    	{
    		cout << arr[i] << " ";
    	}
    	cout << endl;
    }
    void test01()
    {
    	char arr1[] = "helloworld!";
    	int num = sizeof(arr1) / sizeof(arr1[0]);
    	SelectSort(arr1, num);
    	showArray(arr1, num);
    	int arr2[] = { 1, 2, 5, 8, 9, 4, 3, 10, 7, 6};
    	int num2 = sizeof(arr2) / sizeof(arr2[0]);
    	SelectSort(arr2, num2);
    	showArray(arr2, num2);
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    普通函数和函数模板的区别

    • 普通函数调用时可以发生自动类型转换(隐式类型转换);
    • 函数模板在调用时,如果利用自动类型推导,不会发生自动类型转换;
    • 如果利用显示指定类型的方式,可以发生隐式类型转换。

    示例:

    //普通函数和模板函数的区别
    #include 
    using namespace std;
    int myAdd01(int num1, int num2)
    {
    	return num1 + num2;
    }
    template <class T>
    T myAdd02(T num1, T num2)
    {
    	return num1 + num2;
    }
    void test01()
    {
    	int a = 10;
    	int b = 10;
    	char c = 'c';	// c- 99
    	//1.普通函数进行隐式类型转换
    	cout << myAdd01(a, c) << endl;
    	//2.模板函数不能完成隐式类型转换
    	//cout << myAdd02(a, c) << endl;
    	//3.显示指定类型可以完成隐式类型转换
    	cout << myAdd02<int>(a, c) << endl;
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    总结:建议使用显示指定类型的方式,去调用函数模板,因为这样可以自己指定通用类型T

    普通函数和函数模板的调用规则

    • 如果函数模板和普通函数都可以实现,优先调用普通函数;
    • 可以通过空模板参数列表来强制调用函数模板,空模板是指<>中的内容为空,语法:函数名<>(参数列表);
    • 函数模板也可以发生重载;
    • 如果函数模板可以产生更好的匹配优先调用函数模板。

    示例:

    // 普通函数和函数模板的调用规则
    #include 
    using namespace std;
    void myPrint(int a, int b)
    {
    	cout << "普通函数的调用" << endl;
    }
    template <class T>
    void myPrint(T a, T b)
    {
    	cout << "函数模板的调用" << endl;
    }
    template <typename T>
    void myPrint(T a, T b, T c)	//函数模板的重载
    {
    	cout << "函数模板重载的调用" << endl;
    }
    void test01()
    {
    	int a = 10;
    	int b = 10;
    	//1.优先调用普通函数
    	myPrint(a, b);
    	//2.可以通过空模板参数列表强制调用函数模板
    	myPrint<>(a, b);
    	//3.函数模板可以发生重载
    	myPrint(a, b, 100);
    	//4.函数模板可以产生更好的匹配,优先调用函数模板
    	char c = 'c';
    	myPrint(c, c);
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    函数模板的局限性,函数模板的通用性并不是万能的,比如对于自定义类型Person,函数模板是不具有通用性的。因此Cpp为了解决这类问题,提供了模板的重载,可以为这些特定的类型提供具体化的模板。具体化是,显示具体化的原型和定义是以template<>开头,语法就是在重载的函数模板的函数定义前加上==tempalte<>==。并通过名称来指出类型,具体化的方式优于常规模板。示例:

    //模板的局限性
    #include 
    #include 
    using namespace std;
    template <typename T>
    bool myCompare(T& a, T& b)
    {
    	if (a == b)
    		return true;
    	else
    		return false;
    }
    void myPrint(bool flag)
    {
    	if (flag == true)
    		cout << "两者相等" << endl;
    	else
    		cout << "两者不等" << endl;
    }
    class Person
    {
    public:
    	int m_Age;
    	string m_Name;
    	Person(string name, int age)
    	{
    		this->m_Age = age;
    		this->m_Name = name;
    	}
    };
    //具体化,为特定类型提供函数模板
    template<> bool myCompare(Person& p1, Person& p2)
    {
    	if (p1.m_Age == p2.m_Age and p1.m_Name == p2.m_Name)
    		return true;
    	else
    		return false;
    }
    void test01()
    {
    	int a = 10;
    	int b = 10;
    	myPrint(myCompare(a, b));
    }
    void test02()
    {
    	Person p1("Tom", 10);
    	Person p2("Tom", 10);
    	myPrint(myCompare(p1, p2));
    }
    int main(void)
    {
    	test01();
    	test02();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    2、类模板

    ​ 类模板的作用,是建立一个通用的类,类中的成员数据类型可以不具体制定,用一个虚拟的类型来代表。语法:

    template <typename T>	//也可以使用class代替typename
    • 1
    • 2

    解析:

    • template,声明创建模板;
    • typename,表明其后面的符号是一种数据类型,可以用class来代替;
    • T,通用的数据类型,名称可以替换,通常为大写字母;
    • 类模板需要用显示指定的方式来指定模板参数列表<>,并且只能通过显示指定类型的方式进行使用。

    示例:

    //类模板
    #include 
    #include 
    using namespace std;
    template <class NameType, class AgeType>	//类模板
    class Person
    {
    public:
    	NameType m_Name;
    	AgeType m_Age;
    	Person(NameType name, AgeType age)
    	{
    		this->m_Name = name;
    		this->m_Age = age;
    	}
    	void ShowInfo()
    	{
    		cout << this->m_Name << endl;
    		cout << this->m_Age << endl;
    	}
    };
    void test01()
    {
    	Person<string, int> p1("孙悟空", 999);	//显示指定模板参数列表
    	p1.ShowInfo();
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    总结:类模板和函数模板非常相似,在声明类模板template后面加类,此类称为类模板。

    类模板和函数模板的区别

    • 类模板没有自动类型推导的使用方式,只能显示指定类型;
    • 类模板在模板参数列表中可以有默认参数。

    示例:

    //类模板的使用
    #include 
    using namespace std;
    template <class NameType, class AgeType = int >
    class Person
    {
    public:
    	NameType m_Name;
    	AgeType m_Age;
    	Person(NameType name, AgeType age)
    	{
    		this->m_Name = name;
    		this->m_Age = age;
    	}
    	void ShowInfo()
    	{
    		cout << "姓名:" << this->m_Name << endl;
    		cout << "年龄:" << this->m_Age << endl;
    	}
    };
    void test01()
    {
    	//Person p("hello", 10);	//无法自动推导类型
    	Person <string, double> p2("hello", 22.0);
    	p2.ShowInfo();
    	Person <string>	p3("world", 22);	//age的默认类型为int
    	p3.ShowInfo();
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    类模板中成员函数的创建时机,类模板中成员函数和普通类中成员函数创建时机是有区别的:

    • 普通类中的成员函数一开始就可以创建;
    • 类模板中的成员函数在调用时才创建。

    类模板对象作为函数参数,类模板实例化的对象,向函数传参的方式,一共有三种传入方式:

    • 指定传入的类型——直接显示对象的数据类型(最常用的方式);
    • 参数模板化——将对象中的参数变为模板进行传递;
    • 整个类模板化——将这个对象类型模板进行传递。

    示例:

    // 类模板对象作为函数的参数
    #include 
    using namespace std;
    template <class T1, class T2>
    class Person
    {
    public:
    	T1 m_Name;
    	T2 m_Age;
    	Person(T1 name, T2 age)
    	{
    		this->m_Age = age;
    		this->m_Name = name;
    	}
    	void ShowInfo()
    	{
    		cout << "姓名:" << this->m_Name << endl;
    		cout << "年龄:" << this->m_Age << endl;
    	}
    };
    //1.类模板的对象做函数的参数,并且采用引用参数的方式
    void printPerson1(Person <string, int>&p)
    {
    	p.ShowInfo();
    }
    //2.参数模板化
    template <typename T1, typename T2>
    void printPerson2(Person<T1, T2>& p)
    {
    	p.ShowInfo();
    	cout << "T1的类型为:" << typeid(T1).name() << endl;	//查看类型名称
    	cout << "T2的类型为:" << typeid(T2).name() << endl;
    }
    //3.整个类模板化
    template <class T>
    void printPerson3(T &p)
    {
    	p.ShowInfo();
    	cout << "T的类型为:" << typeid(T).name() << endl;
    }
    void test01()
    {
    	Person<string, int> p("孙悟空", 1000);
    	printPerson1(p);
    	Person <string, int> p2("猪八戒", 999);
    	printPerson2(p2);
    	Person <string, int> p3("唐僧", 998);
    	printPerson3(p3);
    
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    总结:

    • 通过类模板创建的对象,可以有三种方式向函数中进行传参;
    • 使用比较广泛的是第一种,指定传入的类型;
    • 使用typeid(类型).name(),查看指定类型的字符串表示。

    类模板与继承,当类模板碰到继承时,需要注意:

    • 当子类继承的父类是一个类模板时,子类在声明的时候,要制定父类中T的类型;
    • 如果不指定,编译器无法给子类分配内存;
    • 如果想灵活指定父类中T的类型,子类也需变为类模板。

    示例:

    //类模板与继承
    #include 
    using namespace std;
    template <class T>
    class Base
    {
    public:
    	T m_base;
    	Base()
    	{
    		cout << "Base中T的数据类型为:" << typeid(T).name() << endl;
    	}
    };
    //class Son : public Base	//错误的继承,因为必须知道T的类型才能确定Son分配的字节大小
    //1.明确指定父类T的类型的继承
    class Son1 :public Base <int>
    {
    public:
    	Son1()
    	{
    		cout << "Son1中的数据类型固定为int" << endl;
    	}
    
    };
    //2.更改父类T的类型的继承
    //如果想灵活指定父类中T的类型,子类也需要类模板
    template <class T1, class T2>
    class Son2 :public Base <T2>	
    {
    public:
    	T1 m_obj;
    	Son2()
    	{
    		cout << "Son2中T1的数据类型为:" << typeid(T1).name() << endl;
    		cout << "Son2中T2的数据类型为:" << typeid(T2).name() << endl;
    	}
    };
    void test01()
    {
    	Son1 s1;
    	cout << "-------------------------" << endl;
    	Son2<int, char> s2;
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    总结:如果父类是类模板,那么子类在继承的过程中需要明确指定出父类中T的类型。

    类模板成员函数的类外实现,在类外实现类模板的成员函数的时候,需要在类名的后面加上模板参数列表,语法是:template<模板参数列表>; 类名<模板参数列表>::成员函数实现,示例:

    //类模板成员函数类外实现
    #include 
    #include 
    using namespace std;
    template <class T1, class T2>
    class Person
    {
    public:
    	T1 m_Age;
    	T2 m_Name;
    	//成员函数的声明
    	Person(T2 name, T1 age);
    	void ShowInfo();
    };
    //1.构造函数的类外实现
    template <class T1, class T2>
    Person<T1, T2>::Person(T2 name, T1 age)
    {
    	this->m_Age = age;
    	this->m_Name = name;
    }
    //2.成员函数的类外实现
    template <class T1, class T2>
    void Person<T1, T2>::ShowInfo()		//加上模板参数列表
    {
    	cout << "姓名:" << this->m_Name
    		<< "年龄:" << this->m_Age << endl;
    }
    void test01()
    {
    	Person<int, string> p("Tom", 22);
    	p.ShowInfo();
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    类模板份文件编写,产生的问题是类模板中成员函数创建时机是在调用阶段,导致文件编写时链接不到。解决的方法是:

    • 解决方法1:直接包含.cpp源文件;
    • 解决方法2:将声明和实现写到同一个文件中,并更改后缀名为.hpphpp是约定的名称,并不是强制的。

    示例:

    person.hpp中代码:

    #pragma once
    #include 
    #include 
    using namespace std;
    template <class T1, class T2>
    class Person
    {
    public:
    	T1 m_Age;
    	T2 m_Name;
    	Person(T1 age, T2 name);
    	void ShowInfo();
    };
    template <class T1, class T2>
    Person<T1, T2>::Person(T1 age, T2 name)
    {
    	this->m_Age = age;
    	this->m_Name = name;
    }
    template <class T1, class T2>
    void Person<T1, T2>::ShowInfo()
    {
    	cout << "姓名:" << this->m_Name
    		<< " 年龄:" << this->m_Age << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    person.h中的代码:

    #pragma once
    #include 
    #include 
    using namespace std;
    template <class T1, class T2>
    class Person
    {
    public:
    	T1 m_Age;
    	T2 m_Name;
    	Person(T1 age, T2 name);
    	void ShowInfo();
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    person.cpp中的代码:

    #include "person.h"
    template 
    Person::Person(T1 age, T2 name)
    {
    	this->m_Age = age;
    	this->m_Name = name;
    }
    template 
    void Person::ShowInfo()
    {
    	cout << "姓名:" << this->m_Name
    		<< " 年龄:" << this->m_Age << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    main.cpp中的代码:

    //类模板分文件编写问题及解决方式
    #include 
    #include 
    //1.解决方式:直接包含源文件
    //#include "person.cpp"
    //2.解决方法:将.h和.cpp中的内容写到一起,将后缀改为.hpp文件
    #include "person.hpp"
    void test01()
    {
    	Person<int, string> p(22, "Tom");
    	p.ShowInfo();
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    类模板与友元,需要掌握类模板配合友元函数的类内和类外实现:

    • 全局函数类内实现,直接在类内声明友元即可;
    • 全局函数类外实现,需要提前让编译器知道全局函数的存在。

    示例:

    //类模板与友元
    #include 
    #include 
    using namespace std;
    //提前让编译器知道Person类的存在
    template <class T1, class T2>
    class Person;
    //提前让编译器知道该全局函数的存在
    template <class T1, class T2>
    void printPerson2(Person<T1, T2>& p);
    //实现Person模板类
    template <class T1, class T2>
    class Person
    {
    	//1.全局函数的类内实现
    	friend void printPerson1(Person <T1, T2> &p)
    	{
    		cout << "姓名:" << p.m_Name
    			<< " 年龄:" << p.m_Age << endl;
    	}
    	//2.全局函数类外实现,加上空模板的参数列表(必须)
    	//如果全局函数是类外实现的,那么需要编译器提前知道全局函数的存在
    	friend void printPerson2<>(Person<T1, T2>& p);
    private:
    	T1 m_Name;
    	T2 m_Age;
    public:
    	Person(T1 name, T2 age)
    	{
    		this->m_Name = name;
    		this->m_Age = age;
    	}
    };
    //2.全局函数的类外实现
    template <class T1, class T2>
    void printPerson2(Person<T1, T2>& p)
    {
    	cout << "姓名:" << p.m_Name
    		<< " 年龄:" << p.m_Age << endl;
    }
    void test01()
    {
    	Person<string, int> p("Jane", 22);
    	printPerson1(p);
    	printPerson2(p);
    }
    
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    ​ 类模板案例,实现一个通用的数组,

    myarray.hpp内容:

    //类模板案例,实现一个通用的数组
    #pragma once
    #include 
    using namespace std;
    
    template <class T>
    class MyArray
    {
    private:
    	T* pAddress;		//指针指向堆区开辟的空间
    	int m_Capacity;		//存放数组容量
    	int m_Size;			//存放数组中的数据个数
    public:
    	//有参构造函数
    	MyArray(int capacity)
    	{
    		//cout << "MyArray的有参构造" << endl;
    		this->m_Capacity = capacity;
    		this->m_Size = 0;
    		this->pAddress = new T[capacity];
    	}
    	//析构函数
    	~MyArray()
    	{
    		//cout << "MyArray的析构函数" << endl;
    		if (this->pAddress != NULL)
    		{
    			delete[] this->pAddress;
    			this->pAddress = NULL;
    		}
    	}
    	//拷贝构造函数
    	MyArray(const MyArray &arr)
    	{
    		//cout << "MyArray的拷贝构造" << endl;
    		this->m_Capacity = arr.m_Capacity;
    		this->m_Size = arr.m_Size;
    		this->pAddress = new T[arr.m_Capacity];		//深拷贝
    		for (int i = 0; i < arr.m_Size; i++)
    		{
    			this->pAddress[i] = arr.pAddress[i];
    		}
    	}
    	//重载运算符 =
    	MyArray& operator=(const MyArray& arr)
    	{
    		//cout << "operator=的调用" << endl;
    		if (this->pAddress != NULL)	//如果以前存在数据则先进行释放
    		{
    			delete[] this->pAddress;
    			this->pAddress = NULL;
    		}
    		this->m_Capacity = arr.m_Capacity;
    		this->m_Size = arr.m_Size;
    		this->pAddress = new T[arr.m_Capacity];		//深拷贝
    		for (int i = 0; i < arr.m_Size; i++)
    		{
    			this->pAddress[i] = arr.pAddress[i];
    		}
    		return *this;		//返回自身的引用
    	}
    	//尾插法
    	void insert_back(const T &value)
    	{
    		if (this->m_Size == this->m_Capacity)
    		{
    			cout << "容量已满" << endl;
    			return;
    		}
    		this->pAddress[this->m_Size] = value;	//在数组尾部插入元素
    		this->m_Size++;	//数组长度+1
    	}
    	//尾删法
    	void delete_back()
    	{
    		if (this->m_Size == 0)
    		{
    			return;
    		}
    		this->m_Size--;	//访问不到最后一个元素,逻辑尾删
    	}
    	//头插法
    	void insert_head(const T& value)
    	{
    		if (this->m_Size == this->m_Capacity)
    		{
    			cout << "容量已满" << endl;
    			return;
    		}
    		for (int i = this->m_Size; i>=0; i--)
    		{
    			this->pAddress[i + 1] = this->pAddress[i];
    		}
    		*this->pAddress = value;	//在数组头部插入元素
    		this->m_Size++;	//数组长度+1
    	}
    	//头删法
    	void delete_head()
    	{
    		if (this->m_Size == 0)
    		{
    			return;
    		}
    		for (int i = 0; i < this->m_Size; i++)
    		{
    			this->pAddress[i] = this->pAddress[i + 1];
    		}
    		this->m_Size--;
    	}
    	//通过下标来进行访问,重载运算符[]
    	T& operator[](int idx)
    	{
    		return this->pAddress[idx];
    	}
    	//返回容量大小
    	int getCapacity()
    	{
    		return this->m_Capacity;
    	}
    	//返回现存的大小
    	int getSize()
    	{
    		return this->m_Size;
    	}
    };
    class Person
    {
    public:
    	string m_Name;
    	int m_Age;
    	Person() {}
    	Person(string name, int age)
    	{
    		this->m_Age = age;
    		this->m_Name = name;
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137

    main.cpp的内容:

    #include "myarray.hpp"	
    void printArrayInt(MyArray<int>& arr)
    {
    	for (int i = 0; i < arr.getSize(); i++)
    	{
    		cout << arr[i] << " ";
    	}
    	cout << endl;
    }
    void test01()
    {
    	//测试
    	MyArray<int> arr1(11);
    	for (int i = 0; i < 5; i++)
    	{
    		arr1.insert_back(i);	//尾插法
    	}
    	printArrayInt(arr1);
    	cout << "arr1[2] = " << arr1[2] << endl;	//测试运算符[]
    	for (int i = 0; i < 5; i++)
    	{
    		arr1.insert_head(i);	//头插法
    	}
    	printArrayInt(arr1);
    	cout << "arr1[5] = " << arr1[5] << endl;	//测试运算符[]
    	arr1.delete_back();		//尾删法
    	printArrayInt(arr1);
    	arr1.delete_head();		//头删法
    	printArrayInt(arr1);
    }
    void printArrayPerson(MyArray<Person>& arr)
    {
    	for (int i = 0; i < arr.getSize(); i++)
    	{
    		cout << "姓名:" << arr[i].m_Name
    			<< " 年龄:" << arr[i].m_Age << endl;
    	}
    	cout << "---------------------" << endl;
    }
    void test02()
    {
    	MyArray<Person> arr1(10);
    	Person p1("Tom", 22);
    	Person p2("Alice", 23);
    	Person p3("Mike", 22);
    	arr1.insert_back(p1);
    	arr1.insert_back(p1);
    	arr1.insert_back(p2);
    	arr1.insert_back(p3);
    	printArrayPerson(arr1);
    	arr1.insert_head(p2);
    	printArrayPerson(arr1);
    	arr1.delete_back();
    	printArrayPerson(arr1);
    	arr1.delete_head();
    	printArrayPerson(arr1);
    }
    int main(void)
    {
    	//test01();
    	test02();
    	return 0;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    (2)STL

    1、STL初识

    ​ Cpp的面向对象和泛型编程思想,目的就是复用性的提升,大多数情况下,数据结构和算法都未能有统一的标准,为了建立数据结构和算法的标准,从而诞生了STL。

    ​ STL的基本概念:

    • STL(Standard Template Library,标准模板库);
    • STL从广义上分为,容器(container)、算法(algorithm)和迭代器(iterator);
    • 容器和算法之间通过迭代器进行无缝连接;
    • STL几乎所有的代码都采用了模板类或者模板函数。

    ​ STL的六大组件:STL大致分为六大组件,分别是容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器

    1、容器:各种数据结构,如vector、list、deque、set、map等,用于存放数据;

    2、算法:各种常用的算法,如sort、find、copy、for_each等;

    3、迭代器:扮演了容器与算法之间的胶合剂;

    4、仿函数:行为类似函数,可以作为算法的某种策略;

    5、适配器:一种用来修饰容器或者仿函数或迭代器接口的东西;

    6、空间配置器:负责空间的配置和管理。

    容器(Container):置物之所,STL容器就是将运用最广泛的一些数据结构实现出来,常用的数据结构有:数组、链表、树、栈、队列、集合、映射等。这些容器又分为序列式容器关联式容器两种。

    • 序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置;
    • 关联式容器:是二叉树的结构,各元素之间没有严格的物理上的顺序关系。

    算法(Algorithm):问题之解法,通过有限的步骤,解决逻辑上或数学上的问题,算法分为质变算法和非质变算法两种。

    • 质变算法:是指运算过程中会更改区间内的元素的内容,例如拷贝、替换、删除等;
    • 非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历等。

    若需要使用STL中的算法,则必须包含STL中算法的头文件,#include

    迭代器(Iterator):容器和算法之间粘合剂,迭代器提供一种方法,使之能够依序寻访某个容器所含的各个元素,而有无需暴露该容器的内部表示方式。每个容器都有自己专属的迭代器。可以将迭代器理解为指针。

    迭代器的种类:

    种类功能支持运算符
    输入迭代器对数据的只读访问只读、支持++、==、!=
    输出迭代器对数据的只写访问只写、支持++
    前向迭代器读写操作、并能向前推进迭代器读写、支持++、==、!=
    双向迭代器读写操作、并能向前和向后操作读写、支持++
    随机访问迭代器读写操作、可以以跳跃的方式访问任意的数据、功能最强的迭代器读写、支持`++、–、[n]、-n、<、<=、>、>=

    常用的容器中迭代器种类为双向迭代器、随机访问迭代器。

    2、常用容器
    A、vector容器

    容器:vector

    头文件:#include

    算法:for_each

    迭代器:vector::iterator

    vector数据结构和数据非常相似,也称为单端数组,单端数组顾名思义是在数组的尾端进行插入和删除,vector与普通数组的区别在于处在数组是静态空间,而verctor可以动态扩展,动态扩展并不是在原有空间的基础上续接新空间,而是寻找更大的内存空间,然后将原数组拷贝到新空间,并且释放原空间。vector容器的迭代器是支持随机访问的迭代器。

    在这里插入图片描述

    vector容器的简单使用

    遍历vector容器的方式示例:

    //vector容器存放内置数据类型
    #include 
    #include 
    #include 
    using namespace std;
    void myprint(int val)
    {
    	cout << val << endl;
    }
    void test01()
    {
    	vector<int> v;		//默认构造,创建数据类型为int的vector容器
    	v.push_back(10);	//尾插法插入数据
    	v.push_back(20);
    	v.push_back(30);
    	v.push_back(40);
    	//第一种遍历方式
    	//vector::iterator itBegin = v.begin();
    	//vector::iterator itEnd = v.end();
    	//while (itBegin != itEnd)
    	//{
    	//	cout << *itBegin << endl;
    	//	itBegin++;
    	//}
    	
    	//第二种遍历方式使用for循环(推荐)
    	//for (vector::iterator it = v.begin(); it != v.end(); it++)
    	//{
    	//	cout << *it << endl;
    	//}
    
    	//第三种遍历方式,利用STL中提供的遍历算法(for_each),需包含头文件,并且提供进行操作的函数
    	for_each(v.begin(), v.end(), &myprint);
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    vector容器存放自定义数据类型的示例:

    //vector容器存放自定义数据类型
    #include 
    #include 
    #include 
    #include 
    using namespace std;
    class Person
    {
    public:
    	string m_Name;
    	int m_Age;
    	Person() {}
    	Person(string name, int age)
    	{
    		this->m_Name = name;
    		this->m_Age = age;
    	}
    };
    //1.存放自定义数据类型
    void test01()
    {
    	vector<Person> v;
    	Person p1("Tom", 22);
    	Person p2("Alice", 22);
    	Person p3("Jane", 22);
    	v.push_back(p1);
    	v.push_back(p2);
    	v.push_back(p3);
    	for (vector<Person>::iterator it = v.begin(); it != v.end(); it++)
    	{
    		//cout << "姓名:" << (*it).m_Name << "\t年龄:" << (*it).m_Age << endl;
    		cout << "姓名:" << it->m_Name
    			<< "\t年龄:" << it->m_Age << endl;
    	}
    }
    //2.存放自定义数据类型的指针
    void test02()
    {
    	vector<Person*> v;
    	Person* ptr1 = new Person("Tom", 22);
    	Person* ptr2 = new Person("Alice", 22);
    	Person* ptr3 = new Person("Hellen", 22);
    	v.push_back(ptr1);
    	v.push_back(ptr2);
    	v.push_back(ptr3);
    	for (vector<Person*>::iterator it = v.begin(); it != v.end(); it++)
    	{
    		cout << "姓名:" << (*it)->m_Name << "\t年龄:" << (*it)->m_Age << endl;
    	}
    
    }
    int main(void)
    {
    	//test01();
    	test02();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    vector容器中再嵌套vector容器示例:

    //vector容器中嵌套容器
    #include 
    #include 
    #include 
    using namespace std;
    void test01()
    {
    	vector <vector<int>> V;	//创建大容器
    	vector <int> v1;	//创建小容器
    	vector <int> v2;
    	vector <int> v3;	
    	for (int i = 0; i < 4; i++)	//向小容器中添加数据
    	{
    		v1.push_back(i+1);
    		v2.push_back(i+5);
    		v3.push_back(i+9);
    	}
    	V.push_back(v1);		//向大容器中添加数据
    	V.push_back(v2);
    	V.push_back(v3);
    	for (vector<vector<int>>::iterator It = V.begin(); It != V.end(); It++)	//外层迭代器指针
    	{
    		// (*It) 是 vector类型
    		for (vector<int>::iterator it = (*It).begin(); it != (*It).end(); it++)	//内层迭代器指针
    		{
    			cout << *it << " ";
    		}
    		cout << endl;
    	}
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    vector的构造函数,函数原型:

    • vector v; //采用模板实现类实现,默认构造函数;
    • vector(itertor beg, iterator end); //将[beg, end)区间内的元素拷贝给vector,左闭右开;
    • vector (n, elem); //构造函数将n个elem拷贝给v本身;
    • vector (const vector& vec); //拷贝构造函数,利用vec来构造vctor。

    示例:

    //vector容器构造
    #include 
    #include 
    using namespace std;
    void printVector(vector<int>& v)
    {
    	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
    	{
    		cout << *it << " ";
    	}
    	cout << endl;
    }
    void test01()
    {
    	//1.默认构造
    	vector <int> v1;
    	for (int i = 0; i < 10; i++)
    	{
    		v1.push_back(i + 1);
    	}
    	printVector(v1);
    	//2.通过区间的方式构造
    	vector<int> v2(v1.begin(), v1.end());
    	printVector(v2);
    	//3.将n个elem拷贝给本身构造
    	vector<int> v3(5, 3);
    	printVector(v3);
    	//4.拷贝构造
    	vector<int> v4(v3);
    	printVector(v4);
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    vector赋值操作,函数原型:

    • vector& operator=(const vector &vec); //重载=操作符,进行赋值;
    • assign(beg, end); //将[beg, end)区间中的数据拷贝赋值给本身;
    • assign(n, elem); //将n个elem拷贝赋值给本身。

    示例:

    //vector的赋值操作
    #include 
    #include 
    using namespace std;
    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 + 1);
    	}
    	printVector(v1);
    	//赋值 operator=
    	vector <int> v2 = v1;
    	printVector(v2);
    	//assign
    	vector <int> v3;
    	v3.assign(v1.begin(), v1.end());
    	printVector(v3);
    	//n个elem
    	vector<int> v4;
    	v4.assign(4, 1);
    	printVector(v4);
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    vector容器大小操作,函数原型:

    • empty(); //判断容器是否为空;
    • capacity(); //返回容器的容量;
    • size(); //返回容器中元素的个数;
    • resize(int num); //重新指定容器的长度为num,若容器变长,则以默认值0填充新的位置;若容器变短,则末尾超出容器长度的元素被删除;
    • resize(int num, elem); //重新指定容器的长度为num,若容器变长,则以elem填充新位置;若容器变短,则末尾超出容器长度的元素被删除。

    示例:

    //vector容器容量和大小的操作
    #include 
    #include 	
    using namespace std;
    void printVector(vector<int>& v)
    {
    	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
    	{
    		cout << *it << " ";
    	}
    	cout << endl;
    }
    void test01()
    {
    	vector<int> v;
    	cout << v.empty() << endl;		//如果为空输出1,不为空输出0
    	v.push_back(1);
    	v.push_back(1);
    	v.push_back(1);
    	v.push_back(1);
    	v.pop_back();
    	printVector(v);
    	cout << v.capacity() << endl;		//返回容量
    	cout << v.size() << endl;			//返回当前元素个数
    	v.resize(6);
    	printVector(v);
    	v.resize(8, 2);			//用2来填充
    	printVector(v);
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    vector插入和删除,函数原型:

    • push_back(elem); //尾插法插入元素elem;
    • pop_back(); //尾部弹出最后一个元素;
    • insert(const_iterator pos, elem); //迭代器指向位置pos插入元素elem;
    • insert(const_iterator pos, int count, elem); //迭代器指向位置pos插入count个元素elem;
    • erase(const_iterator pos); //删除迭代器指向的元素;
    • erase(const_iterator start, const_iterator end); //删除迭代器[start, end)之间的元素;
    • clear(); //删除容器中所有的元素。

    示例:

    //vector容器的插入和删除操作
    #include 
    #include 
    using namespace std;
    void printVector(vector<int>& v)
    {
    	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
    	{
    		cout << *it << " ";
    	}
    	cout << endl;
    }
    void test01()
    {
    	vector<int> v;
    	//尾插法
    	v.push_back(10);
    	v.push_back(10);
    	v.push_back(10);
    	v.push_back(10);
    	printVector(v);
    	//尾删法
    	v.pop_back();
    	printVector(v);
    	//插入 第一个参数是迭代器
    	v.insert(v.begin(), 20);
    	printVector(v);
    	v.insert(v.end(), 2, 30);
    	printVector(v);
    	//删除 第一个参数是迭代器
    	v.erase(v.begin());
    	printVector(v);
    	v.erase(v.begin(), v.end());		//与clear()一致	
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    vector数据存取,函数原型:

    • operator[](int idx); //重载[]运算符,返回索引idx所指的元素;
    • at(int idx); //返回索引idx所指的数据;
    • front(); //返回容器中第一个数据元素;
    • back(); //返回容器中最后一个数据元素。

    示例:

    //vector容器的存取
    #include 
    #include 
    using namespace std;
    void printVector(vector<int>& v)
    {
    	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
    	{
    		cout << *it << " ";
    	}
    	cout << endl;
    }
    void test01()
    {
    	vector <int> v;
    	for (int i = 0; i < 10; i++)
    	{
    		v.push_back(i + 1);
    	}
    	printVector(v);
    	//取元素
    	cout << v[2] << endl;
    	cout << v.at(3) << endl;
    	cout << v.front() << endl;
    	cout << v.back() << endl;
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    vector互换容器,功能是实现两个容器内元素的互换操作,函数原型:

    • swap(vec); //将vec与本身的元素进行互换。

    示例:

    //vector容器的互换
    #include 
    #include 
    using namespace std;
    void printVector(vector<int>& v)
    {
    	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
    	{
    		cout << *it << " ";
    	}
    	cout << endl;
    }
    //1.基本使用
    void test01()
    {
    	vector<int> v1;
    	for (int i = 0; i < 10; i++)
    	{
    		v1.push_back(i + 1);
    	}
    	printVector(v1);
    	vector<int> v2;
    	for (int i = 10; i > 0; i--)
    	{
    		v2.push_back(i);
    	}
    	printVector(v2);
    	v2.swap(v1);
    	cout << "交换后" << endl;
    	printVector(v1);
    	printVector(v2);
    }
    //2.实际用途,巧用swap可以收缩内存空间
    void test02()
    {
    	vector<int> v1;
    	for (int i = 0; i < 100000; i++)
    	{
    		v1.push_back(i + 1);
    	}
    	cout << "v1的容量为:" << v1.capacity() << endl;
    	cout << "v1的大小为:" << v1.size() << endl;
    	v1.resize(3, 0);		//重新指定大小后,大小变化,但是容量不变
    	cout << "v1的容量为:" << v1.capacity() << endl;
    	cout << "v1的大小为:" << v1.size() << endl;
    	//巧用swap来收缩内存空间
    	//vector(v1)相当于调用拷贝构造函数,来创建了一个匿名对象x
    	//然后根据v1的大小和容量来创建了这个匿名对象,这个对象的大小和容量都是3
    	//再使用swap进行交换v1和匿名对象的空间,匿名对象在使用完毕后就被系统回收,这样就收缩了v1的空间
    	vector<int>(v1).swap(v1);
    	cout << "v1的容量为:" << v1.capacity() << endl;
    	cout << "v1的大小为:" << v1.size() << endl;
    }
    int main(void)
    {
    	//test01();
    	test02();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    vector预留空间,功能是减少vector在动态扩展容量时的扩展次数,函数原型:

    • reserve(int len); //容器预留len哥元素长度,预留位置不被初始化,元素也不可被访问。

    示例:

    //vector容器预留空间
    #include 
    #include 
    using namespace std;
    void test01()
    {
    	vector<int> v1;
    	int num1 = 0;		//统计开辟空间的次数
    	int* p1 = NULL;
    	for (int i = 0; i < 100000; i++)
    	{
    		v1.push_back(i + 1);
    		if (p1 != &v1[0])
    		{
    			p1 = &v1[0];
    			num1++;
    		}
    	}
    	cout << "num1 = " << num1 << endl;
    	//预留100000个空间
    	vector<int> v2;
    	v2.reserve(100000);
    	int num2 = 0;		//统计开辟空间的次数
    	int* p2 = NULL;
    	for (int i = 0; i < 100000; i++)
    	{
    		v2.push_back(i + 1);
    		if (p2 != &v2[0])
    		{
    			p2 = &v2[0];
    			num2++;
    		}
    	}
    	cout << "num2 = " << num2 << endl;
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    B、string容器

    容器:striing

    头文件:#include

    string是Cpp风格的字符串,而string的本质是一个类。stringchar*的区别在于:char*是一个指针;而string是一个类,类内部封装了char*,来管理这个字符,是一个char*的容器。 特点在于:string类内部封装了很多的方法,例如查找find、拷贝copy、删除delete、替换replace和插入insertstring管理char*所分配的内存,不用担心复制越界和取值越界等,由类的内部进行负责。

    string的构造函数,构造函数原型:

    • string(); //创建一个空的字符串 例如:string str
    • string(const char* s); //使用字符串s初始化;
    • string(const string& str); //使用一个string对象初始化另一个string对象;
    • string(int n, char c); //使用n个字符c初始化。

    示例:

    //string容器的4种构造函数
    #include 
    #include 
    using namespace std;
    void test01()
    {
    	string s1;		//默认构造函数
    	const char* str = "Hello World";
    	string s2(str); 
    	cout << "s2 = " << s2 << endl;
    	string s3(s2);
    	cout << "s3 = " << s3 << endl;
    	string s4(10, 'c');
    	cout << "s4 = " << s4 << endl;
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    string赋值操作,赋值的函数原型:

    • string& operator=(const char* s); //重载=操作符,char*类型字符串赋值给当前的字符串;
    • string& operator=(const string &str); //重载=操作符,把字符串str赋值给当前的字符串;
    • string& operator=(char c); //重载=操作符,把字符值赋值给当前的字符串;
    • string& assign(const char* s); //把字符串s赋值给当前的字符串
    • string& assign(const char* s, int n); //把字符串s的前n个字符赋值给当前的字符串;
    • string& assign(const strng& str); //把字符串srt赋值给当前字符串;
    • string& assign(int n, char c); //用n个字符c赋值给当前字符串。

    string的赋值方式有很多,一般来说operator=的这种赋值方式最常用,示例:

    //string容器的7种赋值操作
    #include 
    #include 
    using namespace std;
    void test01()
    {
    	string str1;
    	str1 = "Hello World!";
    	cout << "str1 = " << str1 << endl;
    	string str2;
    	str2 = str1;
    	cout << "str2 = " << str2 << endl;
    	string str3;
    	str3 = 'c';
    	cout << "str3 = " << str3 << endl;
    	string str4;
    	str4.assign("Hello Cpp");
    	cout << "str4 = " << str4 << endl;
    	string str5;
    	str5.assign("Hello Cpp", 4);
    	cout << "str5 = " << str5 << endl;
    	string str6;
    	str6.assign(str5);
    	cout << "str6 = " << str6 << endl;
    	string str7;
    	str7.assign(10, 'c');
    	cout << "str7 = " << str7 << endl;
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    string字符串拼接,函数原型:

    • string& operator+=(const char* s); //重载+=操作符;
    • string& operator+=(const char c); //重载+=操作符;
    • string& operator+=(const string& str); //重载+=操作符;
    • string& append(const char* s); //把字符串s连接到当前字符串结尾;
    • string& append(const char* s, int n); //把字符串s的前n个字符连接到当前字符串的结尾;
    • string& append(const string& str); //同operator+=(const string& str)
    • string& append(const string& str, int pos, int n); //字符串str从pos开始的n个字符连接到字符串结尾。

    string的字符串拼接操作有很多,一般来说最常用的是重载operator+=,示例:

    //string容器的7种拼接操作
    #include 
    #include 	
    using namespace std;
    void test01()
    {
    	string str1("Hello ");
    	str1 += "World!";
    	cout << "str1 = " << str1 << endl;
    	string str2 = str1;
    	str2 += 'A';
    	cout << "str2 = " << str2 << endl;
    	string str3 = str1;
    	str3 += str1;
    	cout << "str3 = " << str3 << endl;
    	string str4("Hello ");
    	str4.append("Cpp");
    	cout << "str4 = " << str4 << endl;
    	string str5("Hello ");
    	str5.append("Cpp", 2);
    	cout << "str5 = " << str5 << endl;
    	string str6("Cpp");
    	str6.append(str1);
    	cout << "str6 = " << str6 << endl;
    	string str7;
    	str7.append(str6, 1, 4);
    	cout << "str7 = " << str7 << endl;
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    string查找和替换,函数原型:

    • int find(const string& str, int pos = 0) const; //查找str第一次出现的位置,从pos开始查找;
    • int find(const char* s, int pos = 0) const; //查找s第一次出现的位置,从pos开始查找;
    • int find(const char* s, int pos, int n) const; //从pos位置查找s的前n个字符第一次出现的位置;
    • int find(const char c, int pos = 0) const; //查找字符c第一次出现的位置;
    • int rfind(const string& str, int pos = npos) const; //查找str最后一次位置,从pos开始查找;
    • int rfind(const char* s, int pos = npos) const; //查找s最后一次出现的位置,从pos开始查找;
    • int rfind(const char* s, int pos, int n) const; //从pos查找s的前n个字符最后一次出现的位置;
    • int rfind(const char c, int pos = 0) const; //查找从pos开始n个字符为字符串str;
    • string& replace(int pos, int n, const string& str); //替换从pos开始n个字符为字符串str;
    • string& replace(int pos, int n, const char* s); //替换从pos开始的n个字符为字符串s

    总结:注意,如果没有查找到所需的字符串或字符,则返回-1;findrfind的区别在于find是从右往左查找,而rfind是从左往右查找,即rfind保存的是最后一次查找到的位置,而find保存的是第一次查找到的位置。replace在替换的时候,需要指定从什么位置开始替换,替换多少个字符,替换成什么字符串。

    string字符串比较,函数原型:

    • int compare(const string& str) const; //与字符串str比较;
    • int compare(const char* s) const; //与字符串s比较。

    总结:注意,字符串的比较是对字符逐个按照ASCII码进行比较,=返回0>返回1<返回-1。字符串比较主要是用于判断两个字符串是否相等,判断谁打谁小的意义并不是很大。

    string字符存取,函数原型:

    • char operator[](int n); //重载[]操作符,通过[]方式存取;
    • char& at(int n); //通过at方式存取。

    总结:可以通过上述两种方式来进行访问和修改,示例:

    //string字符存取
    #include 
    #include 
    using namespace std;
    void test01()
    {
    	string str("Hello World!");
    	//1.通过[]访问
    	for (int i = 0; i < str.size(); i++)	//string.size()返回字符串的长度
    	{
    		cout << str[i] << " ";
    	}
    	cout << endl;
    	//2.通过at访问
    	for (int i = 0; i < str.size(); i++)	
    	{
    		cout << str.at(i) << " ";
    	}
    	cout << endl;
    	//对字符串进行修改
    	str[0] = 'A';
    	cout << str << endl;
    	str.at(1) = 'B';
    	cout << str << endl;
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    string插入和删除,函数原型:

    • string& insert(int pos, const char* s); //插入字符串s;
    • string& insert(int pos, const string& str); //插入字符串str;
    • string& insert(int pos, int n, char c); //在指定位置pos插入n个字符;
    • string& erase(int pos, int n = npos); //删除从pos开始的n个字符。

    string子串,函数原型:

    • string substr(int pos = 0, int n = npos) const; //返回由pos开始的n个字符组成的字符串。

    总结:子串常用于在字符串中截取一段字符,示例:

    // string子串
    #include 
    #include 
    using namespace std;
    void test01()		//使用操作,从邮箱中截取用户名
    {
    	string str = "shenjiahao0610@163.com";
    	int pos = str.find('@');
    	cout << pos << endl;
    	string username = str.substr(0, pos);
    	cout << username << endl;
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    C、deque容器

    容器:deque

    头文件:#include

    迭代器:deque::iterator

    deque容器是双端数组,可以从两端进行插入和删除;dequevector的区别在于:

    • vector对于头部的插入删除效率低下,数据量越大,效率越低;
    • deque相对而言,对头部的插入删除速度会比vector块;
    • vector访问元素时的速度会比deque块,这和两者的内部实现有关。

    在这里插入图片描述

    deque的内部工作原理:deque内部有一个中控器,维护者每段缓冲区中的内容,缓冲区存放真实数据,中控器维护的时每个缓冲区的地址,使得使用deque时像一片连续的内存空间。deque的迭代器也是支持随机访问的。

    在这里插入图片描述

    deque构造函数,函数原型:

    • deque deq; //默认构造函数;
    • deque(iterator beg, iterator end); //构造函数将[beg, end)区间的元素拷贝给本身;
    • deque(n, elem); //构造函数将n个elem拷贝给本身;
    • deque(const deque& deq); //拷贝构造函数。

    示例:

    //deque构造函数
    #include 
    #include 
    using namespace std;
    void printDeque(const deque<int>& deq)
    {
    	//对于const修饰的函数原型,智能使用只读的迭代器const_iterator
    	for (deque<int>::const_iterator it = deq.begin(); it != deq.end(); it++)	
    	{
    		cout << *it << " ";
    	}
    	cout << endl;
    }
    void test01()
    {
    	//1.默认构造函数
    	deque<int> deq1;
    	for (int i = 0; i < 10; i++)
    	{
    		deq1.push_back(i + 1);
    	}
    	printDeque(deq1);
    	//2.以区间的方式
    	deque<int> deq2(deq1.begin(), deq1.end());
    	printDeque(deq2);
    	//3.n个elem的方式
    	deque<int> deq3(3, 10);
    	printDeque(deq3);
    	//4.拷贝构造的方式
    	deque<int> deq4(deq3);
    	printDeque(deq4);
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    总结:deque容器和vector容器的构造方式几乎一样,灵活使用即可。

    deque赋值操作,函数原型:

    • deque& operator=(const deque& deq); //重载=操作符;
    • assign(beg, end); //将[beg, end)区间内的数据拷贝赋值给本身;
    • assign(n, elem); //将n个elem拷贝赋值给本身。

    示例:

    //deque容器赋值操作
    #include 
    #include 
    using namespace std;
    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> deq1;
    	for (int i = 0; i < 10; i++)
    	{
    		deq1.push_back(i + 1);
    	}
    	printDeque(deq1);
    	deque<int> deq2;
    	deq2 = deq1;
    	printDeque(deq2);
    	deque<int> deq3;
    	deq3.assign(deq1.begin(), deq1.end());
    	printDeque(deq3);
    	deque<int> deq4;
    	deq4.assign(5, 10);
    	printDeque(deq4);
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    deque大小操作,函数原型:

    • empty(); //判断deque容器是否为空;
    • size(); //返回deque容器中元素的个数;
    • resize(num); //重新指定deque容器的长度为num,若容器变长,则以默认值填充位置;若容器变短,则末尾超出容器长度的元素被删除。
    • resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem填充新位置;若容器变短,则末尾超出容器长度的元素被删除。

    示例:

    //deque容器的大小操作
    #include 
    #include 
    using namespace std;
    void printDeque(const deque<int>& d)
    {
    	for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++)
    	{
    		cout << *it << " ";
    	}
    	cout << endl;
    }
    void Isempty(const deque<int>& d)
    {
    	if (d.empty())
    	{
    		cout << "deque is empty" << endl;
    	}
    	else
    		cout << "deque isn't empty" << endl;
    }
    void test01()
    {
    	deque<int> deq1;
    	Isempty(deq1);
    	for (int i = 0; i < 10; i++)
    	{
    		deq1.push_back(i + 1);
    	}
    	Isempty(deq1);
    	printDeque(deq1);
    	cout << "deque has " << deq1.size() << " element" << endl;
    	deq1.resize(5);
    	printDeque(deq1);
    	cout << "deque has " << deq1.size() << " element" << endl;
    	deq1.resize(8, 2);
    	printDeque(deq1);
    	cout << "deque has " << deq1.size() << " element" << endl;
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    deque插入和删除,函数原型:

    • push_back(elem); //在deque容器尾部插入一个数据;
    • push_front(elem); //在deque容器头部插入一个数据;
    • pop_back(); //删除容器最后一个数据;
    • pop_front(); //删除容器第一个数据;
    • insert(const_iterator pos, elem); //在pos位置插入一个elem元素的拷贝,返回新的数据的位置;
    • insert(const_iterator pos, n, elem); //在pos位置插入n个elem元素,无返回值;
    • insert(const_iterator pos, beg, end); //在pos位置插入[beg, end)区间的数据,无返回值;
    • erase(const_iterator start, const_iterator end); //删除[start, end)区间的数据,返回下一个数据的位置;
    • erase(const_iterator pos); //删除pos位置的数据,返回下一个数据的位置。

    示例:

    //deque容器的插入和删除
    #include 
    #include 
    using namespace std;
    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> deq1;
    	deq1.push_back(10);
    	deq1.push_front(5);
    	deq1.push_back(2);
    	printDeque(deq1);
    	deq1.pop_back();
    	deq1.pop_front();
    	printDeque(deq1);
    	//插入一个元素
    	deq1.insert(deq1.begin(), 3);
    	printDeque(deq1);
    	//插入n个elem
    	deq1.insert(deq1.end(), 2, 10);
    	printDeque(deq1);
    	//插入一个区间
    	deq1.insert(deq1.begin(), deq1.begin(), deq1.end());
    	printDeque(deq1);
    	//删除指定位置的元素
    	deque<int>::const_iterator iter = deq1.begin();
    	iter++;
    	deq1.erase(iter);
    	printDeque(deq1);
    	//删除指定区间内的元素
    	deq1.erase(iter, deq1.end());
    	printDeque(deq1);
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    deque数据存取,函数原型:

    • operator[](int idx); //重载[]运算符,返回idx索引指向的元素;
    • at(int idx); //返回索引idx所指向的元素;
    • front(); //返回容器中的第一个元素;
    • back(); //返回容器中最后一个元素。

    deque排序操作,算法#include ,算法原型:

    • sort(const_iterator beg, const_iterator end); //对beg和end区间内的元素进行排序,默认从小到大排序。对于支持随机访问迭代器的容器,都可以利用sort算法来直接进行排序,vector容器也可以利用sort进行排序。

    示例:

    //deque容器的排序
    #include 
    #include 
    #include 
    using namespace std;
    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> deq;
    	deq.push_back(3);
    	deq.push_back(2);
    	deq.push_back(4);
    	deq.push_back(1);
    	deq.push_back(5);
    	printDeque(deq);
    	sort(deq.begin(), deq.end());
    	printDeque(deq);	//排序后
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    D、stack容器

    stack是一种先进后出(First in first out, FIFO)的数据结构,又称为堆栈,它只有一个出口就是在它的栈顶,栈中只有栈顶的元素才可以被外界使用,因此栈是不允许有遍历行为的。

    在这里插入图片描述

    stack常用接口,构造函数

    • stack stk; //stack采用类模板实现,stack对象的默认构造函数;
    • stack(const stack& stk); //拷贝构造函数;

    赋值操作

    • stack& operator=(const stack& stk); //重载=操作符,执行赋值运算;

    数据存取

    • push(elem); //向栈顶添加元素;
    • pop(); //从栈顶移除第一个元素;
    • top(); //返回栈顶元素;

    大小操作

    • empty(); //判断堆栈是否为空;
    • size(); //返回栈的大小。

    示例:

    //stack容器
    #include 
    #include 
    using namespace std;
    void test01()
    {
    	stack<int> stk;
    	stk.push(10);
    	stk.push(20);
    	stk.push(30);
    	while (!stk.empty())
    	{
    		cout << "当前有 " << stk.size() << " 个元素"
    			<< "当前栈顶元素为 " << stk.top() << endl;
    		stk.pop();
    	}
    	cout << "当前栈中元素个数为 " << stk.size() << endl;
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    E、queue容器

    queue容器是以中国先进先出(First in first out,FIFO)的数据机构,又称为队列,它有两个出口

    在这里插入图片描述

    从队尾进入,从队头出去,因此只有队头和队尾能够被外界访问,队列是不允许有遍历行为的。

    queue常用接口,构造函数

    • queue que; //queue采用类模板实现,queue对象的默认构造函数;
    • queue(const queue& que); //拷贝构造函数;

    赋值操作

    • queue& operator=(const queue& que); //重载=赋值操作符;

    数据存取

    • push(elem); //往队尾添加元素;
    • pop(); //移除队头的第一个元素;
    • back(); //返回队尾最后一个元素;
    • front(); //返回第一个元素。

    大小操作

    • empty(); //判断堆栈是否为空;
    • size(); //返回栈的大小。

    示例:

    //queue容器
    #include 
    #include 
    #include 
    using namespace std;
    class Person
    {
    public:
    	string m_Name;
    	int m_Age;
    	Person(string name, int age)
    	{
    		this->m_Age = age;
    		this->m_Name = name;
    	}
    	void ShowInfo()
    	{
    		cout << "姓名:" << this->m_Name
    			<< "\t年龄:" << this->m_Age << endl;
    	}
    };
    void test01()
    {
    	Person p1("Tom", 22);
    	Person p2("Alice", 21);
    	Person p3("Baby", 22);
    	Person p4("Hello", 20);
    	Person p5("World!", 22);
    	queue<Person> que;
    	que.push(p1);
    	que.push(p2);
    	que.push(p3);
    	que.push(p4);
    	que.push(p5);
    	while (!que.empty())
    	{
    		cout << "当前队列元素个数为:" << que.size() << " 当前的队头元素:\t";
    		que.front().ShowInfo();
    		que.pop();
    	}
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    F、list容器

    list容器是将数据进行链式存储,是一种链表的数据结构,链表是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的。链表的组成:链表由一系列结点组成;结点的组成:结点包含一个存储数据元素的数据域,还有一个指向下一个结点地址的指针域。在STL中实现的链表是一个双向循环链表

    在这里插入图片描述

    由于链表的存储方式并不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,则链表的迭代器属于双向迭代器。list的优点如下:

    • 采用动态内存分配,不会造成内存浪费和溢出;
    • 链表执行插入和删除操作非常方便,只需要修改指针即可,不需要移动大量元素。

    list的缺点如下:

    • 链表灵活,但是空间(指针域)和时间(遍历)额外消耗较大。

    list容器有一个重要的性质,插入操作和删除操作都不会造成原有list迭代器的失效,这是在vector容器中不成立的。

    总结:STL中listvector是最常用的两个容器,它们各有优缺点。

    list构造函数,函数原型:

    • list lst; //list采用模板类实现,对象的默认构造函数;
    • list(iterator beg, iterator end); //构造函数将[beg, end)区间中的元素拷贝给本身;
    • list(n, elem); //拷贝构造函数将n个elem拷贝给本身;
    • list(const list& lst); //拷贝构造函数。

    示例:

    //list容器的构造函数
    #include 
    #include 
    using namespace std;
    void printList(const list<int>& lst)
    {
    	for (list<int>::const_iterator it = lst.begin(); it != lst.end(); it++)
    	{
    		cout << *it << " ";
    	}
    	cout << endl;
    }
    void test01()
    {
    	list<int> lst1;			//1.默认构造函数
    	for (int i = 0; i < 10; i++)
    	{
    		lst1.push_back(i + 1);
    	}
    	printList(lst1);
    	lst1.pop_back();
    	list<int> lst2(lst1);   //2.拷贝构造函数
    	printList(lst2);
    	list <int>::iterator iter = lst2.begin();
    	list<int> lst3(++iter, lst2.end());		//3.拷贝构造函数
    	printList(lst3);
    	list<int> lst4(4, 5);	//4.n个elem构造函数
    	printList(lst4);
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    list赋值和交换,函数原型:

    • assign(iterator beg, iterator end); //将[beg, end)区间内的数据拷贝赋值给本身;
    • assign(n, elem); //将n个elem拷贝赋值给本身;
    • list& operator=(const list& lst); //重载=运算符;
    • swap(lst); //将lst与本身的元素互换,与vector容器的互换操作类似。

    示例:

    //list赋值和交换
    #include 
    #include 
    using namespace std;
    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> lst1;
    	for (int i = 0; i < 10; i++)
    	{
    		lst1.push_back(10 * (i + 1));
    	}
    	printList(lst1);
    	list<int>::iterator It = lst1.begin();
    	list<int> lst2;			//1.重载operator=赋值
    	lst2 = lst1;
    	list<int> lst3;
    	lst3.assign(lst1.begin(), lst1.end());	//2.assign的方式
    	printList(lst3);
    	printList(lst2);
    	lst1.assign(5, 1);		//3.将n个elem赋值给lst1
    	printList(lst1);
    	lst1.swap(lst2);		//4.swap交换
    	printList(lst2);
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    list大小操作,函数原型:

    • size(); //返回容器中元素的个数;
    • empty(); //判断容器是否为空;
    • resize(num); //重新指定容器的长度为num,若容器变长,则以默认值填充新的位置;若容器变短,则末尾超出容器长度的元素被删除;
    • resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem填充新的位置;若容器变短,则末尾超出容器长度的元素被删除。

    示例:

    //list容器的大小操作
    #include 
    #include 
    using namespace std;
    void printList(const list<int>& L)
    {
    	cout << "当前链表中的元素个数为:" << L.size() << endl;
    	for (list<int>::const_iterator it = L.begin(); it != L.end(); it++)
    	{
    		cout << *it << " ";
    	}
    	cout << endl;
    }
    void test01()
    {
    	list<int> lst;
    	for (int i = 0; i < 5; i++)
    	{
    		lst.push_back(10 * (i + 1));
    	}
    	printList(lst);
    	if (!lst.empty())
    	{
    		lst.resize(6);
    		printList(lst);
    	}
    	lst.resize(10, 0);
    	printList(lst);
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    list插入和删除,函数原型:

    • push_back(elem); //在容器尾部加入一个元素;
    • pop_back(); //删除容器中最后一个元素;
    • push_front(elem); //在容器的开头插入一个元素;
    • pop_front(); //删除容器中第一个元素;
    • insert(const_iterator pos, elem); //在pos位置插入elem元素的拷贝,返回新的数据的位置;
    • insert(const_iterator pos, n, elem); //在pos位置插入n个elem数据,无返回值;
    • insert(const_iterator pos, iterator beg, iterator end); //在pos位置插入[beg, end)区间的数据,无返回值;
    • clear(); //移除容器中所有的数据;
    • erase(iterator beg, iterator end); //删除[beg, end)区间中的数据,返回下一个数据的位置;
    • erase(iterator pos); //删除pos位置的数据,返回下一个数据的位置;
    • remove(elem); //删除容器中 所有与elem值匹配的元素。

    示例:

    //list容器的插入和删除
    #include 
    #include 
    using namespace std;
    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> lst;
    	//1.插入
    	lst.push_back(10);
    	lst.push_front(20);
    	printList(lst);
    	//2.删除
    	lst.pop_back();
    	lst.pop_front();
    	//3.插入
    	lst.insert(lst.begin(), 5);
    	printList(lst);
    	lst.insert(lst.end(), 5, 10);
    	printList(lst);
    	lst.insert(lst.end(), lst.begin(), lst.end());
    	printList(lst);
    	//4.清除
    	lst.erase(lst.begin());
    	printList(lst);
    	//lst.erase(lst.begin(), lst.end());		//与lst.clear()等价
    	//5.删除指定元素
    	lst.remove(10);
    	printList(lst);
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    list数据存取list容器并没有重载[],也不能使用at来进行访问,原因是因为,list是链表并不是一段连续的线性空间,list的迭代器也不支持随机访问,函数原型:

    • front(); //返回第一个元素;
    • back(); //返回最后一个元素。

    示例:

    //list容器的存取
    #include 
    #include 
    using namespace std;
    void test01()
    {
    	list <int> lst;
    	lst.push_back(10);
    	lst.push_back(20);
    	lst.push_back(30);
    	cout << "第一个元素: " << lst.front() << endl;
    	cout << "最后一个元素: " << lst.back() << endl;
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    list反转和排序,函数原型:

    • reverse(); //反转链表;
    • sort(); //链表排序。

    示例:

    //list反转和排序
    #include 
    #include 
    #include 
    using namespace std;
    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 v1, int v2)		//回调函数,自定义比较方式,让排序为降序
    {
    	return v1 > v2;
    }
    void test01()
    {
    	list<int> lst;
    	lst.push_back(10);
    	lst.push_back(40);
    	lst.push_back(30);
    	lst.push_back(20);
    	lst.push_back(50);
    	printList(lst);
    	lst.reverse();		//倒序
    	printList(lst);
    	//注:所有不支持随即迭代器的容器,不能够使用STL标准算法algorithm
    	//例如这样做是错的
    	//sort(lst);
    	lst.sort();		//应该调用lst自己的排序方法,默认升序
    	printList(lst);
    	lst.sort(myCompare);		//更改为降序
    	printList(lst);
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    排序案例,对自定义Person类型进行排序,首先按照年龄来进行排序,如果年龄相同则按照身高来进行排序。

    //list排序案例
    #include 
    #include 
    #include 
    using namespace std;
    //按照年龄来进行排序,如果年龄相同则按照身高来进行排序
    class Person
    {
    public:
    	int m_Age;
    	double m_Height;
    	string m_Name;
    	Person(string name, int age, double height)
    	{
    		this->m_Age = age;
    		this->m_Height = height;
    		this->m_Name = name;
    	}
    	void ShowInfo() const		//常函数
    	{
    		cout << "姓名 " << this->m_Name
    			<< "\t年龄 " << this->m_Age
    			<< "\t身高 " << this->m_Height << endl;
    	}
    };
    void printList(const list<Person>& L)		//参数是一个常对象
    {
    	for (list<Person>::const_iterator it = L.begin(); it != L.end(); it++)
    	{
    		it->ShowInfo();
    	}
    }
    //bool myCompare(Person* p1, Person* p2)		//排序回调函数,参数类型不能为指针,否则会报错
    //{
    //	if (p1->m_Age == p2->m_Age)
    //	{
    //		return p1->m_Height < p2->m_Height;
    //	}
    //	else
    //		return p1->m_Age < p2->m_Age;
    //}
    bool myCompare(Person& p1, Person& p2)		//排序回调函数
    {
    	if (p1.m_Age == p2.m_Age)
    	{
    		return p1.m_Height < p2.m_Height;
    	}
    	else
    		return p1.m_Age < p2.m_Age;
    }
    void test01()
    {
    	Person p1("Tom", 22, 183.0);
    	Person p2("Alice", 22, 180.5);
    	Person p3("Eric", 20, 190.1);
    	Person p4("Motty", 18, 180.0);
    	Person p5("Rick", 22, 184);
    	Person p6("Jane", 23, 180.2);
    	list<Person> lst;
    	lst.push_back(p1);
    	lst.push_back(p2);
    	lst.push_back(p3);
    	lst.push_back(p4);
    	lst.push_back(p5);
    	lst.push_back(p6);
    	printList(lst);
    	lst.sort(myCompare);
    	cout << "------------------------" << endl;
    	printList(lst);
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    G、set/multiset容器

    set基本概念set被称为集合,其中所有的元素都会在插入时被自动进行排序(针对内置数据类型),本质上set/multiset属于关联容器,底层结构是用二叉树进行实现的。两者区别在于:

    • set中不允许有重复的元素;
    • multiset中允许有重复的元素。

    set的构造和赋值,构造函数,函数原型:

    • set st; //默认构造函数;
    • set(const set &st); //拷贝构造函数。

    赋值操作,函数原型:

    • set& operator=(const set& st); //重载=运算符,进行拷贝操作。

    示例:

    //set构造和拷贝
    #include 
    #include 
    using namespace std;
    void printSet(const set<int>& S)
    {
    	for (set<int>::const_iterator it = S.begin(); it != S.end(); it++)
    	{
    		cout << *it << " ";
    	}
    	cout << endl;
    }
    void test01()
    {
    	//1.默认构造函数
    	set<int> st1;		
    	//set容器在插入的时候,只有insert方法
    	st1.insert(10);
    	st1.insert(30);
    	st1.insert(20);
    	st1.insert(40);
    	st1.insert(40);		//set容器不支持插入重复的值,不会报错,但是插入不成功
    	printSet(st1);		//set容器会自动排序
    	//2.拷贝构造函数
    	set<int> st2(st1);
    	printSet(st2);
    	//3.赋值运算
    	set<int> st3;
    	st3 = st1;
    	printSet(st3);
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    总结

    • set容器插入数据时用insert
    • set容器插入数据时会对数据进行自动排序。

    set大小操作和交换,函数原型:

    • size(); //返回容器中元素的数目;
    • empty(); //判断容器是否为空;
    • swap(); //交换两个集合容器,类似于vectorlist

    示例:

    //set容器大小和交换操作
    #include 
    #include 
    using namespace std;
    void printSet(const set<int>& S)
    {
    	if (S.empty())
    	{
    		cout << "集合为空" << endl;
    		return;
    	}
    	cout << "集合的元素个数:" << S.size() << endl;
    	for (set<int>::const_iterator it = S.begin(); it != S.end(); it++)
    	{
    		cout << *it << " ";
    	}
    	cout << endl;
    }
    void test01()
    {
    	set<int> st1;
    	st1.insert(10);
    	st1.insert(20);
    	st1.insert(30);
    	st1.insert(40);
    	printSet(st1);
    	set<int> st2;
    	st2.swap(st1);	//交换集合中元素
    	printSet(st1);
    	printSet(st2);
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    set插入和删除,函数原型:

    • insert(elem); //在容器中插入元素,只有这种插入方式;
    • clear(); //清除容器中所有元素;
    • erase(iterator pos); //删除pos迭代器所指向的元素,返回指向下一个元素的迭代器;
    • erase(iterator beg, iterator end); //删除区间[beg, end)的所有元素,返回指向下一个元素的迭代器;
    • erase(elem); //删除容器中值为elem的元素。

    示例:

    //set容器插入和删除操作
    #include 
    #include 
    using namespace std;
    void test01()
    {
    	set<int> st1;
    	st1.insert(10);
    	st1.insert(20);
    	st1.insert(30);
    	st1.erase(st1.begin());
    	st1.erase(20);		//删除st1中的
    	st1.erase(st1.begin(), st1.end());//st1.clear();
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    set查找和统计,函数原型:

    • find(key); //查找key是否存在,若存在,返回指向该键元素的迭代器;若不存在则返回set.end();
    • count(key); //统计key的元素个数,返回的结果只有0或1。

    示例:

    //set的查找和统计
    #include 
    #include 
    using namespace std;
    void printSet(const set<int>& S)
    {
    	for (set<int>::const_iterator it = S.begin(); it != S.end(); it++)
    	{
    		cout << *it << " ";
    	}
    	cout << endl;
    }
    void findElem(const set<int>& S, int elem)
    {
    	set<int>::iterator It = S.find(elem);
    	if (It != S.end())
    	{
    		cout << "查找的元素为: " << *It << endl;
    	}
    	else
    		cout << "查找失败" << endl;
    }
    void coutElem(const set<int>& S, int elem)
    {
    	cout << "统计元素 " << elem << " 的个数为 " << S.count(elem) << endl;
    }
    void test01()
    {
    	set<int> st1;
    	for (int i = 0; i < 10; i++)
    	{
    		st1.insert(i + 1);
    	}
    	printSet(st1);
    	findElem(st1, 5);
    	findElem(st1, 11);
    	coutElem(st1, 5);
    	coutElem(st1, 11);
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    setmultiset的区别

    • set不可以插入重复数据,而multiset可以;
    • set插入数据的同时会返回插入结果,返回值的类型是一个对组,表示插入是否成功;
    • multiset不会检测数据,因此可以插入重复数据。

    示例:

    //set和multiset的区别
    #include 
    #include 
    using namespace std;
    void test01()
    {
    	set<int> st;	//不允许插入重复的数据
    	pair<set<int>::iterator, bool> ret = st.insert(10);		//set用对组接受insert的返回值
    	if (ret.second == true)
    		cout << "插入成功" << endl;
    	else
    		cout << "插入失败" << endl;
    	ret = st.insert(10);		//不能重复插入数据
    	if (ret.second == true)
    		cout << "插入成功" << endl;
    	else
    		cout << "插入失败" << endl;
    	multiset<int> mset;
    	mset.insert(10);		//multiset用iterator接受insert的返回值
    	mset.insert(10);
    	for (multiset<int>::iterator it = mset.begin(); it != mset.end(); it++)
    	{
    		cout << *it << " ";
    	}
    	cout << endl;
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    pair对组的创建,对组用于描述成对出现的数据,利用对组可以返回两个数据,函数原型:

    • pair p(value1, value2); //有参构造函数;
    • pair p = make_pair(value1, value2); //利用make_pair函数进行构造,可以单独使用make_pair来生成对组而无需指定模板参数,make_pair会自动生成模板参数。

    pair对组的访问

    • .first //访问对组中第一个元素;
    • .second //访问对组中第二个元素。

    示例:

    //pair的使用
    #include 
    using namespace std;
    void test01()
    {
    	//1.有参构造创建对组
    	pair<string, int> p1("Tom", 22);
    	cout << "姓名:\t" << p1.first << "\t年龄:\t" << p1.second << endl;
    	//2.make_pair创建对组
    	pair<string, int> p2 = make_pair("Alice", 21);
    	cout << "姓名:\t" << p2.first << "\t年龄:\t" << p2.second << endl;
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    set容器排序规则更改set容器默认的排序规则是从小到大,我们利用仿函数可以改变排序的规则。


    H、map/multimap容器

    map容器中所有的元素都是pairpair中第一个元素key(键值),起到索引的作用,第二个元素value(实值);所有的元素都会根据元素的键值进行自动排序。(类似于python中的dict)。map/multimap的本质都属于关联式容器,底层结构是用二叉树进行实现的。使用map/multimap的优点是可以根据key值快速找到value值。map/multimap的区别在于:

    • map不允许容器中有重复的key值元素;
    • multimap允许容器中有重复的key值元素。

    map构造和赋值,函数原型:

    • map mp; //map默认构造函数,其中T1是key的类型,T2是value的类型;
    • map(const map& mp); //map拷贝构造函数;
    • map& operator=(const map& mp); //重载=运算符。

    示例:

    //map容器的构造和赋值
    #include 
    #include 	
    #include 
    using namespace std;
    void printMap(const map<string, int>& M)
    {
    	for (map<string, int>::const_iterator it = M.begin(); it != M.end(); it++)
    	{
    		cout << "Key: " << it->first
    			<< "\tValue: " << it->second << endl;
    	}
    	cout << "-----------------------------" << endl;
    }
    void test01()
    {
    	map<string, int> mp;		//1.默认构造函数
    	mp.insert(pair<string, int>("a", 10));
    	mp.insert(pair<string, int>("b", 20));
    	mp.insert(pair<string, int>("Hello", 50));
    	mp.insert(pair<string, int>("World!", 100));
    	printMap(mp);
    	//2.拷贝构造函数
    	map<string, int> mp2(mp);
    	printMap(mp2);
    	//3.赋值操作
    	map<string, int> mp3 = mp;
    	printMap(mp3);
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    map大小和交换操作,函数原型:

    • .size(); //返回容器中元素的数目;
    • .empty(); //判断容器是否为空;
    • .swap(mp); //交换两个map容器。

    示例:

    //map容器的大小操作
    #include 
    #include 
    using namespace std;
    void printMap(const map<int, int> M)
    {
    	if (M.empty())
    		cout << "map容器为空" << endl;	//1.大小操作
    	else
    	{
    		cout << "map容器不为空" << endl;
    		cout << "元素个数为:" << M.size() << endl;
    	}
    	for (map<int, int>::const_iterator it = M.begin(); it != M.end(); it++)
    	{
    		cout << "key:" << it->first
    			<< "\tvalue:" << it->second << endl;
    	}
    	cout << "----------------------" << endl;
    }
    void test01()
    {
    	map<int, int> mp;
    	mp.insert(pair<int, int>(1, 10));
    	mp.insert(pair<int, int>(2, 20));
    	mp.insert(pair<int, int>(3, 30));
    	printMap(mp);
    	map<int, int> mp2;
    	mp2.insert(pair<int, int>(5, 10));
    	mp.swap(mp2);		//2.交换操作
    	printMap(mp);
    	printMap(mp2);
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    map插入和删除,函数原型:

    • insert(elem); //在容器中插入元素;
    • clear(); //清除所有元素;
    • erase(iterator pos); //删除pos迭代器所指向的元素,返回下一个元素的位置的迭代器;
    • erase(iterator beg, iterator end); //删除区间[beg, end)的所有元素,返回下一个元素的迭代器;
    • erase(key); //删除容器中值为key的元素

    示例:

    //map插入和删除
    #include 
    #include 
    #include 
    using namespace std;
    void printMap(const map<string, int>& M)
    {
    	if (M.empty())
    		cout << "map为空" << endl;
    	else
    	{
    		for (map<string, int>::const_iterator it = M.begin(); it != M.end(); it++)
    		{
    			cout << "Key:" << it->first
    				<< "\tValue:" << it->second << endl;
    		}
    		cout << "------------------------------" << endl;
    	}
    }
    void test01()
    {
    	map<string, int> mp;
    	//插入操作
    	//1.pair插入方式
    	mp.insert(pair<string, int>("Alice", 22));
    	//2.make_pair插入方式
    	mp.insert(make_pair("Eirc", 20));
    	//3.value_type
    	mp.insert(map<string, int>::value_type("Jerry", 23));
    	//4.重载[]的方式
    	mp["Jane"] = 21;
    	printMap(mp);
    
    	//删除操作
    	//1.erase(pos)
    	map<string, int>::iterator iter = mp.begin();
    	mp.erase(++iter);
    	printMap(mp);
    	//2.erase(elem)
    	mp.erase("Alice");
    	printMap(mp);
    	//3.erase(beg, end)  //3.clear()
    	mp.erase(mp.begin(), mp.end());
    	printMap(mp);
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    map的查找和统计,函数原型:

    • operator[](T1 key); //重载[]运算符,返回key所指向的value;
    • find(key); //查找key是否存在,若存在则返回该键元素的迭代器;若不存在则返回set.end()
    • count(key); //统计key的元素个数,因为map不允许存在重复的键key,因此统计的结果只能为0或1;multimap允许存在重复的键,因此统计的结果可能大于1。

    示例:

    //map查找和统计
    #include 
    #include 
    using namespace std;
    void printMap(const map<string, int>& M)
    {
    	for (map<string, int>::const_iterator it = M.begin(); it != M.end(); it++)
    	{
    		cout << "Key:" << it->first
    			<< "\tValue:" << it->second << endl;
    	}
    	cout << "------------------------------" << endl;
    }
    void test01()
    {
    	map<string, int> mp;
    	mp.insert(make_pair("Tom", 22));
    	mp.insert(pair<string, int>("Alice", 21));
    	mp.insert(map<string, int>::value_type("Jack", 23));
    	mp["Eric"] = 24;
    	printMap(mp);
    	//1.find查找
    	map<string, int>::iterator pos = mp.find("Jack");
    	if (pos != mp.end())
    		cout << pos->first << " " << pos->second << endl;
    	else
    		cout << "未找到该元素" << endl;
    	//2.count统计
    	cout << mp.count("Hello") << endl;
    	cout << mp.count("Tom") << endl;
    }
    int main(void)
    {
    	test01();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
  • 相关阅读:
    业务团队为何要实施CRM系统?
    【JavaSE】static关键词
    牛视系统源码定制,抖音矩阵系统定制开发。come here
    24 | ping 命令的使用
    函数基础学习01
    网络是怎样连接的--DNS服务器查询原理
    Linux 学习笔记(yum)
    0903(046天 线程集合总结01)
    从数据库发展史看数据库未来技术趋势
    2022最新 MySQL 内部技术架构面试题
  • 原文地址:https://blog.csdn.net/qq_44940689/article/details/126686391