C++提高编程(1)

时间:2023-04-06 18:49:59

C++提高编程

1.模板

1.1模板的概念

  • 模板就是建立通用的摸具大大提高复用性
  • 模板的特点
    • 模板不可以直接使用,它只是一个框架
    • 模板的通用并不是万能的

1.2函数模板

  • C++中另一种编程思想称为泛型编程,主要利用的技术就是模板
  • C++提供两种模板机制:函数模板和类模板

1.2.1函数模板语法

  • 函数模板作用

    • 建立一个通用函数,其函数返回值类型形参类型可以不具体定制,用一个虚拟的类型来代表
  • 语法

template<typename T>
函数声明或定义
  • 解释
    • template — 声明创建模板
    • typename — 表面起后面的符号是一种数据类型,可以用class替代
    • T — 通用的数据类型加粗样式,名称可以替换,通常为大写字母
#include<iostream>
using namespace std;

//函数模板
template<typename T> //声明一个模板,告诉编译器后面代码紧跟的T不要报错,T是一个通用类型
void mySwap(T& a, T& b) 
{
	T temp = a;
	a = b;
	b = temp;
}

void test1()
{
	// 两种方式使用函数模板
	// 1.自动类型推导
	int a = 20;
	int b = 40;
	mySwap(a, b);
	cout << "a = " << a << endl; // 40
	cout << "b = " << b << endl; // 20

	// 2.显示指定类型
	mySwap<int>(a, b);
	cout << "a = " << a << endl; // 20
	cout << "b = " << b << endl; // 40
}

int main() 
{
	test1();
	system("pause");
	return 0;
}

1.2.2函数模板注意事项

  • 注意事项
    • 自动类型推导,必须推导出一致的数据类型T,才可以使用
    • 模板必须要确定出T的数据类型,才可以使用
#include<iostream>
using namespace std;

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

//模板必须要确定出T的数据类型,才可以使用
template<typename T>
void func()
{
	cout << "func的调用" << endl;
}


void test1() {
	int a = 10;
	int b = 30;
	char c = 'a';
	mySwap(a, b);
	// mySwap(a, c); // error,推导不出不一致的T类型
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
}
void test2() 
{
	func<int>();
}


int main()
{
	//test1();
	test2();
	system("pause");
	return 0;
}

1.2.3函数模板案例

#include<iostream>
using namespace std;

template<typename T>
void mySwap(T &a,T &b)
{
	T temp = a;
	a = b;
	b = temp;
}
template<typename T>
void arrSort(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 showArr(T arr[], int len)
{
	for (int i = 0;i < len;i++) {
		cout << arr[i] << " ";
	}
	cout << endl;
}
void test1() 
{
	char arr[] = "abefcej";
	arrSort<char>(arr, sizeof(arr) / sizeof(char));
	showArr(arr, sizeof(arr) / sizeof(char));
}
int main()
{
	test1();
	system("pause");
	return 0;
}

1.2.4普通函数与函数模板的区别

  • 区别
    • 普通函数调用时可以发生自动类型转换(隐式类型转换)
    • 函数模板调用时,如果利用自动类型推导不会发生隐式类型转换
    • 如果利用显示指定类型的方式,可以发生隐式类型转换
#include<iostream>
using namespace std;

//函数模板
template<typename T>
T myAdd2(T a, T b)
{
	return a + b;
}

// 普通函数
int myAdd(int a, int b) {
	return a + b;
}
void test1() {
	int a = 12;
	int b = 13;
	char c = 's';
	cout << myAdd(a, b) << endl;// 25
	// 隐藏类型转化
	cout << myAdd(a, c) << endl;// 127


	cout << myAdd2(a, b) << endl;// 25
	// 自动类型推导  -- 不能自动转换(隐式转换)
	//cout << myAdd2(a, c) << endl;// error
	
	//显示指定类型
	cout << myAdd2<int>(a, c) << endl;// 127
}

int main() {
	test1();
	system("pause");
	return 0;
}

总结: 建议使用显示指定类型的方式调用函数模板,自己可以确定通用类型

1.2.5普通函数与函数模板的调用规则

  • 调用规则
    • 如果函数模板普通函数都可以实现,优先调用普通函数
    • 可以通过空模板参数列表来强制调用函数模板
    • 函数模板也可以发生重载
    • 如果函数模板可以产生更好的匹配优先调用函数模板
#include<iostream>
using namespace std;

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 test1()
{
	int a = 10;
	int b = 20;
	//函数模板和普通函数都可以调用,优先调用普通函数
	//普通函数只有函数声明,调用会报错
	myPrint(a, b);

	//空模板参数列表,强制调用函数模板
	myPrint<>(a, b);

	//函数模板可以发生重载
	myPrint(a, b, 20);

	//函数模板产生更好的匹配,优先调用函数模板
	char a1 = 'a';
	char b1 = 'b';
	myPrint(a1, b1);// 调用函数模板
}

int main() 
{
	test1();
	system("pause");
	return 0;
}

1.2.6模板的局限性

  • 局限性
    • 模板的通用性并不是万能
  • 利用具体化的模板,可以解决自定义类型的通用化
#include<iostream>
using namespace std;
#include<string>
class Person
{
public:
	Person(string name,int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	string m_Name;
	int m_Age;
};


template<typename T>
bool myCompare(T& a, T& b)
{
	if (a == b) {
		return true;
	}
	else {
		return false;
	}
}
// 利用具体化Person的版本实现代码,具体优先调用
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 test1()
{
	int a = 10;
	int b = 20;
	bool res = myCompare(a, b);
	if (res) {
		cout << "a==b" << endl;
	}
	else {
		cout << "a != b" << endl;
	}
}
void test2()
{
	Person person1("Jack", 20);
	Person person2("Jack", 20);
	bool res = myCompare(person1, person2);
	if (res) {
		cout << "person1 == person2" << endl;
	}
	else {
		cout << "person1 != person2" << endl;
	}
}
int main()
{
	//test1();
	test2();
	system("pause");
	return 0;
}

1.3类模板

1.3.1类模板语法

  • 类模板的作用

    • 建立一个通用类,类中的成员 数据类型 可以不具体规定,用一个虚拟的类型来代表
  • 语法

    template<class T>
    • template — 声明创建模板
    • class — 表明其后免得符号是一种数据类型,可以用 typename 代替
    • T — 通用的数据类型,名称可以替换,通常为大写字幕
#include<iostream>
using namespace std;
#include<string>

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

};
void test1() 
{
	Person<string, int> p1("类模板", 20);
	p1.showPerson();
}

int main()
{
	test1();
	system("pause");
	return 0;
}

1.3.2类模板和函数模板区别

  • 类模板和函数模板的区别
    • 类模板没有自动类型推导的使用方式
    • 类模板模板参数列表中可以有默认参数
#include<iostream>
using namespace std;
 //类模板和函数模板的区别


//类模板在参数列表中可以有默认参数    AgeType = int
template<class NameType,class AgeType = int>
class Person
{
public:
	Person(NameType name,AgeType age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	void showPerson()
	{
		cout << "name:" << this->m_Name << ",age:" << this->m_Age << endl;
	}
	NameType m_Name;
	AgeType m_Age;
};

void test1()
{
	// 类模板没有自动化类型推导使用方式
	//Person p("数字化", 10);
	Person<string, int> p("数字化", 10);
	p.showPerson();
}
void test2()
{
	Person<string> p("智能化", 10);
	p.showPerson();
}
int main()
{
	//test1();
	test2();
	system("pause");
	return 0;
}

1.3.3类模板中成员函数创建时机

  • 类模板中成员函数和普通类中成员函数创建时机是有区别的
    • 普通类成员函数一开始就可以创建
    • 类模板成员函数在调用时才创建
#include<iostream>
using namespace std;

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();
	}
};


void test1() {
	MyClass<Person1> m;
	m.fun1();
	// 类模板中成员函数在调用时创建
	// m.fun2();
}
int main() {
	system("pause");
	return 0;
}

1.3.4类模板对象做函数参数

  • 类模板实例化出的对象,向函数传参的方式
    • 指定传入的类型 — 直接显示对象的数据类型 (推荐
    • 参数模板化 — 将对象中的参数变为模板进行传递
    • 整个类模板化 — 将这个对象类型 模板化 进行传递
#include<iostream>
using namespace std;
template<class T,class K>
class Person
{
public:
	Person(T name, K age) {
		this->m_Name = name;
		this->m_Age = age;
	}
	void showPerson() {
		cout << "name:" << this->m_Name << ",age:" << this->m_Age << endl;
	}
	T m_Name;
	K m_Age;

};
// 1.指定传入类型(推荐)
void printPerson1(Person<string, int> &p)
{
	p.showPerson();
}

void test1() {
	Person<string, int> p("智能化", 120);
	printPerson1(p);
}

// 2.参数模板化
template<class T,class K>
void printPerson2(Person<T, K>& p) {
	p.showPerson();
	//查看推导的类型
	cout << "T的类型为:" << typeid(T).name() << endl; //class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >
	cout << "K的类型为:" << typeid(K).name() << endl; //int
}


void test2() {
	Person<string, int> p("数字化", 10);
	printPerson2(p);
}

// 3.整个类模板化
template<class T>
void printPerson3(T &p) {
	p.showPerson();
	cout << "T的数据类型: " << typeid(T).name() << endl;
}
void test3() {
	Person<string, int> p("自动化", 30);
	printPerson3(p);
}


int main() {
	//test1();
	//test2();
	test3();
	system("pause");
	return 0;
}

1.3.5类模板与继承

  • 类模板碰到继承时,需要注意一下几点
    • 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中的T类型
    • 如果不指定,编译器无法给子类分配内存
    • 如果想灵活指定出父类中的T的类型子类也需要变成类模板
#include<iostream>
using namespace std;
#include<string>


template<class T,class K>
class Person
{
public:
	Person(T name, K age);
		/*{
			this->m_Name = name;
			this->m_Age = age;
		}*/
	void showPerson();
	/*{
		cout << "姓名: " << this->m_Name << ",年龄:" << this->m_Age << endl;
	}*/

	T m_Name;
	K m_Age;

};

//构造函数的类外实现
template<class T,class K>
Person<T,K>::Person(T name, K age) 
{
	this->m_Name = name;
	this->m_Age = age;
}
// 成员函数的类外实现
template<class T,class K>
void Person<T, K>::showPerson() 
{
	cout << "姓名: " << this->m_Name << ",年龄:" << this->m_Age << endl;
}

void test1() 
{
	Person<string,int> p("智能组装", 8);
	p.showPerson();
}
int main() {

	test1();
	system("pause");
	return 0;
}

1.3.6类模板成员函数类外实现

#include<iostream>
using namespace std;
#include<string>


template<class T,class K>
class Person
{
public:
	Person(T name, K age);
		/*{
			this->m_Name = name;
			this->m_Age = age;
		}*/
	void showPerson();
	/*{
		cout << "姓名: " << this->m_Name << ",年龄:" << this->m_Age << endl;
	}*/

	T m_Name;
	K m_Age;

};

//构造函数的类外实现
template<class T,class K>
Person<T,K>::Person(T name, K age) 
{
	this->m_Name = name;
	this->m_Age = age;
}
// 成员函数的类外实现
template<class T,class K>
void Person<T, K>::showPerson() 
{
	cout << "姓名: " << this->m_Name << ",年龄:" << this->m_Age << endl;
}

void test1() 
{
	Person<string,int> p("智能组装", 8);
	p.showPerson();
}
int main() {

	test1();
	system("pause");
	return 0;
}

1.3.7类模板分文件编写

  • 类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到

  • 解决方式:

    • 直接包含.cpp源文件
      • 头文件 person.h
    #pragma once
    #include<iostream>
    using namespace std;
    #include<string>
    template<class T,class K>
    class Person
    {
    public:
    	Person(T name,K age);
    	void showPerson();
    
    	T m_Name;
    	K m_Age;
    
    };
    
      • 源文件 person.cpp
    #include "person.h"
    
    template<class T, class K>
    Person<T, K>::Person(T name, K age)
    {
    	this->m_Name = name;
    	this->m_Age = age;
    }
    
    template<class T, class K>
    void Person<T, K>::showPerson()
    {
    	cout << "姓名: " << this->m_Name << ",年龄:" << this->m_Age << endl;
    }
    
      • 源文件
    #include<iostream>
    using namespace std;
    #include<string>
    
    
    // 1.第一种解决方式,直接包含 源文件
    //#include "person.cpp"
    
    // 2.第二种解决方式,将.h和.cpp中的内容写到一起,将后缀名改为.hpp文件
    #include"person.hpp"
    
    //template<class T,class K>
    //class Person
    //{
    //public:
    //	Person(T name, K age);
    //	void showPerson();
    //	T m_Name;
    //	K m_Age;
    //};
    //
    //template<class T,class K>
    //Person<T, K>::Person(T name, K age)
    //{
    //	this->m_Name = name;
    //	this->m_Age = age;
    //}
    //
    //template<class T,class K>
    //void Person<T, K>::showPerson() 
    //{
    //	cout << "姓名: " << this->m_Name << ",年龄:" << this->m_Age << endl;
    //}
    
    void test1() 
    {
    
    	Person<string,int> person("数据化", 20);
    	person.showPerson();
    }
    
    int main()
    {
    	test1();
    	system("pause");
    	return 0;
    }
    
    • 声明和实现写在同一个文件中,并更改后缀名.hpp,hpp是约定的名称,并不是强制
      • 头文件 person.hpp
      		#pragma once
      #include<iostream>
      using namespace std;
      #include<string>
      template<class T, class K>
      class Person
      {
      public:
      	Person(T name, K age);
      	void showPerson();
      
      	T m_Name;
      	K m_Age;
      
      };
      
      
      template<class T, class K>
      Person<T, K>::Person(T name, K age)
      {
      	this->m_Name = name;
      	this->m_Age = age;
      }
      
      template<class T, class K>
      void Person<T, K>::showPerson()
      {
      	cout << "姓名: " << this->m_Name << ",年龄:" << this->m_Age << endl;
      }
      
      • 引入方式同上

1.3.8类模板与友元

  • 全局函数类内实现 — 直接在类内声明友元即可
  • 全局函数类外实现 — 需要提前让编译器知道全局函数的存在
#include<iostream>
#include<string>
using namespace std;

// 声明Person类
template<class T, class K>
class Person;

//类外实现

template<class T, class K>
void printPerson2(Person<T, K> &p)
{
	cout << "-----------全局函数的类外实现-----------" << endl;
	cout << "姓名: " << p.m_Name << ",年龄: " << p.m_Age << endl;
}


//类模板
template<class T,class K>
class Person
{
	// 全局函数  类内实现
	friend void printPerson(Person<T, K> p)
	{
		cout << "姓名: " << p.m_Name << ",年龄: " << p.m_Age << endl;
	}

	//全局函数 类外实现
	// 加空模板参数列表
	// 全局函数 是类外实现,需要让编译器提前知道这个函数的存在
	friend void printPerson2<>(Person<T, K> &p);

public:
	Person(T name, K age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
private:
	T m_Name;
	K m_Age;
};

// 1.全局函数在类内实现
void test1() 
{
	Person<string, int> p("Rose", 18);
	printPerson(p);
}

// 2.全局函数在类外实现
void test2()
{
	Person<string, int> p("Jack", 20);
	printPerson2(p);
}
int main()
{
	//test1();
	test2();
	system("pause");
	return 0;
}

1.3.9类模板案例

#pragma once
#include<iostream>
using namespace std;

template<class T>
class MyArray
{
public:
	MyArray(int capacity)
	{
		this->m_Capacity = capacity;
		this->m_Size = 0;
		this->pAddress = new T[this->m_Capacity];

	}
	//析构函数
	~MyArray()
	{
		if (this->pAddress != NULL) {
			delete[] this->pAddress;
			this->pAddress = NULL;
		}
	}
	//拷贝构造
	MyArray(const MyArray &arr) 
	{
		this->m_Capacity = arr.m_Capacity;
		this->m_Size = arr.m_Size;
		//this->pAddress = arr.pAddress;
		//深拷贝
		this->pAddress = new T[arr.m_Capacity];
		//将arr中的数据都拷贝过来
		
		for (int i = 0;i < this->m_Size;i++)
		{
			this->pAddress[i] = arr.pAddress[i];
		}

	}
	// operator== 防止浅拷贝问题
	MyArray& operator==(const MyArray& arr)
	{
		//先判断原来堆区是否有数据,如果有先释放
		if (this->pAddress != NULL)
		{
			delete[] this->pAddress;
			this->pAddress = NULL;
			this->m_Capacity = 0;
			this->m_Size = 0;
		}
		//深拷贝
		this->m_Capacity = arr.m_Capacity;
		this->m_Size = arr.m_Size;
		this->pAddress = new T[arr.m_Capacity];
		for(int i = 0;i<this->m_Size;i++)
		{
			this->m_Capacity[i] = arr.m_Capacity[i];
		}
		return *this;
	}

	//尾插法
	void Push_Back(const T &val)
	{
		// 判断容量是否有空间
		if (this->m_Capacity == this->m_Size)
		{
			return;
		}
		this->pAddress[this->m_Size] = val;
		//更新数组大小
		this->m_Size++;
	}
	//尾删法
	void Pop_Back()
	{
		//让用户访问不到最后一个元素,即为尾删,逻辑删除
		if (this->m_Size == 0)
		{
			return;
		}
		this->m_Size--;
	}
	//通过下标方式访问数组的元素
	T& operator[](int index)
	{
		return this->pAddress[index];
	}
	//返回数组的容量
	int getCapacity()
	{
		return this->m_Capacity;
	}
	//返回数组的大小
	int getSize()
	{
		return this->m_Size;
	}
	//遍历数组
	void printArray()
	{
		for (int i = 0;i < this->m_Size;i++)
		{
			cout << this->pAddress[i] << " ";
		}
		cout << endl;
	}

private:
	// 指针指向堆区开辟的真实数组
	T* pAddress;
	// 数据容量
	int m_Capacity;
	//数据大小
	int m_Size;
};



#include<iostream>
using namespace std;
#include "MArray.hpp"

void printArray(MyArray<int> &arr)
{
	for (int i = 0;i < arr.getSize();i++)
	{
		cout << arr[i]<< " ";
	}
	cout << endl;
}

void test1()
{
	MyArray<int> arr(5);
	for (int i = 0;i < 5;i++) 
	{
		//利用尾插法插入数据
		arr.Push_Back(i);
	}
	printArray(arr);

	cout << "arr的容量为: " << arr.getCapacity() << endl;
	cout << "arr的容量为: " << arr.getSize() << endl;

	MyArray<int> arr2(arr);
	cout << "----arr2的打印输出----" << endl;
	printArray(arr2);
	arr2.Pop_Back();
	cout << "----arr2尾删后打印输出----" << endl;
	printArray(arr2);
	cout << "arr2的容量为: " << arr.getCapacity() << endl;
	cout << "arr2的容量为: " << arr.getSize() << endl;
	//遍历数组
	arr2.printArray();


}

int main()
{
	test1();
	system("pause");
	return 0;

}

2.STL初始

2.1STL的诞生

  • C++的面对对象泛型编程思想,目的就是复用性的提升
  • 大多数情况下,数据结构和算法都未能有一套标准,导致*从事大量重复工作
  • 为了建立数据结构和算法的一套标准,诞生了STL

2.2STL的基本概念

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

2.3STL六大组件

  • STL分为六大组件
    • 容器:各种数据结构,如vectorlistdequesetmap等等,用来存放数据
    • 算法:各种常用的算法,如sortfindcopyfor_each
    • 迭代器:扮演了容器与算法之间的胶合剂
    • 仿函数:行为类似函数,可作为算法的某种策略
    • 适配器:一种用来修饰容器仿函数迭代器接口的东西
    • 空间配置器:负责空间的配置与管理

2.4STL容器、算法、迭代器

2.4.1容器

  • STL容器就是运用最广泛的一些数据结构实现出来
  • 常用的数据结构
    • 数组
    • 链表
    • 队列
    • 集合
    • 映射表
  • 容器分为序列式容器关联式容器
    • 序列式容器:强调值得排序,序列式容器中得每个元素均有固定得位置
    • 关联式容器:二叉树结构,各元素之间没用严格得物理上的顺序关系

2.4.2算法

  • 有限的步骤,解决逻辑数学上的问题
  • 算法分为: 质变算法非质变算法
    • 质变算法:是指运算过程中会更改区间内的元素内容,如拷贝、替换、删除
    • 非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等等

2.4.3迭代器

  • 容器和算法之间粘合剂
  • 提供一种方法,使之能够依序寻找某个容器所含的各个元素,而又无暴露该容器的内部表示方式
  • 每个容器都有自己专属的迭代器
  • 迭代器使用非常类似于指针
  • 迭代器种类:
种类 功能 支持运算
输入迭代器 对数据的只读访问 只读,支持++、==、!=
输出迭代器 对数据的只写访问 只写,支持++
向前迭代器 读写操作,并能向前推进迭代器 读写,支持++、==、!=
双线迭代器 读写操作,并能向前和向后操作 读写,支持++、--
随机访问迭代器 读写操作,可以以跳跃的方式访问任意数据,功能最强的迭代器 读写,支持++、--、[n]、-n、<、<=、>、>=
  • 常用的容器迭代器种类为双向迭代器随机访问迭代器

2.5容器算法迭代器初识

2.5.1 vector存放内置数据类型

  • 容器 vector
  • 算法 for_each
  • 迭代器 vector<int>::iterator
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
// vector容器存放内置数据类型


//遍历
void printContainer(int val)
{
	cout << val<<"  ";
}


void test1()
{
	//创建了一个vector容器
	vector<int> vec;

	//向容器中插入数据
	vec.push_back(10);
	vec.push_back(20);
	vec.push_back(30);
	vec.push_back(50);
	vec.push_back(90);
	vec.push_back(20);

	// 通过迭代器访问容器中的数据

	// 起始迭代器  指向容器中第一个元素
	vector<int>::iterator itBegin = vec.begin();
	// 结束迭代器  指向容器中最后一个元素的下一个位置
	vector<int>::iterator itEnd = vec.end();

	//第一种遍历方式
	cout << "while循环遍历" << endl;
	while (itBegin != itEnd)
	{		
		cout << *itBegin<< "  ";
		itBegin++;
	}
	cout << endl;

	//第二种遍历方式
	cout << "for循环遍历容器" << endl;
	for (vector<int>::iterator it = vec.begin();it != vec.end();it++)
	{		
		cout << *it<<"  ";
	}
	cout << endl;

	//第三种遍历方式
	for_each(vec.begin(), vec.end(), printContainer);

}



int main()
{

	test1();
	system("pause");
	return 0;
}

2.5.2vector存放自定义数据类型

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

// vector容器存放自定义数据类型

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

//遍历
void printInfo(Person person)
{
	cout << "标签: " << person.m_Name << ",年限: " << person.m_Age << endl;
}

void test1()
{
	vector<Person> vp;
	Person p1("数字化", 10);
	Person p2("模块化", 23);
	Person p3("拟人化", 1);
	Person p4("智能化", 9);
	Person p5("树脂化", 24);
	Person p6("机械化", 11);


	//向容器添加数据
	vp.push_back(p1);
	vp.push_back(p2);
	vp.push_back(p3);
	vp.push_back(p4);
	vp.push_back(p5);
	vp.push_back(p6);

	//遍历容器中的数据

	// while方式遍历
	cout << "-----------while遍历---------" << endl;
	vector<Person>::iterator itBegin = vp.begin();
	vector<Person>::iterator itEnd = vp.end();
	while (itBegin != itEnd)
	{
		cout << "标签: " << (*itBegin).m_Name << ",年限: " << (*itBegin).m_Age << endl;
		itBegin++;
	}

	// for方式遍历

	cout << "--------for遍历-------" << endl;
	for (vector<Person>::iterator itBegin = vp.begin();itBegin != vp.end();itBegin++)
	{
		cout << "标签: " << (*itBegin).m_Name << ",年限: " << (*itBegin).m_Age << endl;
	}

	//for_each方式遍历
	cout << "--------for_each遍历-------" << endl;
	for_each(vp.begin(), vp.end(), printInfo);
}
int main()
{
	test1();
	system("pause");
	return 0;
}

2.5.3容器嵌套

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
// vector容器存放内置数据类型


//遍历
void printContainer(int val)
{
	cout << val<<"  ";
}


void test1()
{
	//创建了一个vector容器
	vector<int> vec;

	//向容器中插入数据
	vec.push_back(10);
	vec.push_back(20);
	vec.push_back(30);
	vec.push_back(50);
	vec.push_back(90);
	vec.push_back(20);

	// 通过迭代器访问容器中的数据

	// 起始迭代器  指向容器中第一个元素
	vector<int>::iterator itBegin = vec.begin();
	// 结束迭代器  指向容器中最后一个元素的下一个位置
	vector<int>::iterator itEnd = vec.end();

	//第一种遍历方式
	cout << "while循环遍历" << endl;
	while (itBegin != itEnd)
	{		
		cout << *itBegin<< "  ";
		itBegin++;
	}
	cout << endl;

	//第二种遍历方式
	cout << "for循环遍历容器" << endl;
	for (vector<int>::iterator it = vec.begin();it != vec.end();it++)
	{		
		cout << *it<<"  ";
	}
	cout << endl;

	//第三种遍历方式
	for_each(vec.begin(), vec.end(), printContainer);

}



int main()
{

	test1();
	system("pause");
	return 0;
}

3.STL-常用容器

3.1string容器

3.1.1string基本概念

  • 本质:string是c++风格的字符串,而string本质上是一个类
  • string和char *区别
    • char * 是一个指针
    • string是一个类,类内部封装了char * ,管理这个字符串,是一个char *型的容器

3.1.2string构造函数

  • 构造函数原型
    • 创建空字符串
    string();
    
    • 字符串s初始化
    string(const char * s);
    
    • 使用一个字符串初始化另一个字符串
    string(const string & str);
    
    • 使用n个字符c初始化
    string(int n,char c);
    
#include<iostream>

using namespace std;
//string构造函数

void test1()
{
	//1.默认构造函数
	string s1;

	//2.字符串初始化
	const char* str = "hello";
	string s2(str);
	cout << "s2 = " << s2 << endl;

	//3.一个字符串给另一个字符串初始化
	string s3(s2);
	cout << "s3 = " << s3 << endl;

	//使用n个字符初始化;
	string s4(4, 'x');
	cout << "s4 = " << s4 << endl;

}



int main()
{
	test1();
	system("pause");
	return 0;
}

3.1.3string赋值操作

  • 给string字符串进行赋值
  • 赋值的函数原型
    • char*类型字符串 赋值给当前的字符串
    string& operator=(const char *s)
    
    • 把字符串s赋值给当前字符串
    string& operator=(const string &s)
    
    • 字符赋值给当前字符串
    string& operator=(char c)
    
    • 把字符串赋值给当前的字符串
    string & assign(const char *s)
    
    • 把字符串s的前面n个字符赋给当前字符串
    string& assign(const char * s,int n)
    
    • 把字符串s赋给当前字符串
    string& assgin(const string &s)
    
    • 用n个字符c赋给当前字符串
    string& assign(int n,char c)
    
#include<iostream>
using namespace std;
#include<string>
//string赋值操作

void test1()
{
	string str1;
	str1 = "hello";
	cout << "str1 = " << str1 << endl;

	string str2;
	str2 = str1;
	cout << "str2 = " << str2 << endl;

	string str3;
	str3 = 'a';
	cout << "str3 = " << str3 << endl;

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

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

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

	string str7;
	str7.assign(4, 'g');
	cout << "str7 = " << str7 << endl;
}


int main()
{
	test1();
	system("pause");
	return 0;
}

3.1.4string字符拼接

  • 实现在字符串末尾拼接字符串
  • 函数原型
    • 重载+=操作符
    string& operator+=(const char* str)
    string& operator+=(const char c)
    string& operator+=(const string& str)
    
    • 把字符串s连接到当前字符串结尾
    string& append(const char *s)
    
    • 把字符串s的前n个字符连接到当前字符串结尾
    string& append(const char*s,int n)
    
    • 同operator+=(const string &str)
    string& append(const string &s)
    
    • 字符串中从pos开始的n个字符连接到字符换末尾
    string& append(const string &s,int pos,int n)
    
	str7 += " bbb";
	cout << "str7 = " << str7 << endl;

	str7.append("hello", 2, 2);
	cout << "str7 = " << str7 << endl; // gggg bbbll

3.1.5string查找和替换

  • 查找: 查找指定字符串是否存在
  • 替换: 在指定的位置替换字符串
  • 函数原型
    • 查找str字符串第一次出现位置,从pos开始查找
    int find(const string &str,int pos = 0) const
    
    • 查找s第一次出现位置,从pos开始查找
    int find(const char* s,int pos = 0) const
    
    • 从pos位置查找s的前n个字符第一次位置
    int find(const char &s,int pos,int n) const
    
    • 查找字符c第一次出现的位置
    int find(const char c,int pos = 0) const
    
    • 查找str最后一次位置,从pos开始查找
    int rfind(const string &str,int pos = npos) const
    
    • 查找s最后一次出现位置,从pos开始查找
    int rfind(const char * s,int pos = npos) const
    
    • 从pos查找s的前n个字符最后一次位置
    int rfind(const char &s,int pos,int n) const
    
    • 查找字符c最后一次出现的位置
    int rfind(const char c,int pos = 0) const
    
    • 替换从pos开始n个字符为字符串
    string& replace(int pos,int n,const string& str)
    
    • 替换从pos开始的n个字符为字符串s
    string& replace(int pos,int n,const char* s)
    

3.1.6string字符串比较

  • 字符串之间的比较(逐个比较)
  • 比较方式
    • 字符串比较是按字符的ASCII码进行对比
    • 结果
      • = 返回 0
      • > 返回 1
      • < 返回 -1
  • 函数原型
    • 与字符串s比较
    int compare(const string &s) const
    
    • 与字符串s比较
    int compare(const char * s) const
    
	string strs = "hello";
	string strs1 = "hollw";
	int comp = strs.compare(strs1);
	cout << "comp = " << comp << endl;// -1

3.1.7string字符存取

  • string中单个字符存取
  • 通过[ ]方式获取字符
char& operator[](int n)
  • 通过at方式获取字符
char& at(int n)
string strs = "hello";
cout << "字符: " << strs[2] << endl;// l

3.1.8string插入和删除

  • 对string字符串进行插入和删除字符串操作
  • 函数原型
    • 插入字符串
    string& insert(int pos,const char* s)
    
    • 插入字符串
    string& insert(int pos,const string& str)
    
    • 在指定位置插入n个字符c
    string& insert(int pos,int n,char c)
    
    • 删除从pos开始的n个字符
    string& erase(int pos,int n = npos)
    
	string strs = "hello";
	string str12;
	str12 = strs.erase(1, 3);
	cout << "str12 = " << str12 << endl;// ho

3.1.9string子串

  • 从字符串中获取想要的子串
  • 函数原型
    • 返回pos开始的n个字符组成的字符串
    string substr(int pos = 0,int n = npos) const
    
	string strs = "hello";
	string stru;
	stru = strs.substr(1, 3);
	cout << "stru = " << stru << endl;// ell

3.2vector容器

3.2.1vector基本概念

1.功能
  • vector数据结构和数组非常类似,也称为单端数组
2.vector与普通数组区别
  • 不同之处在于数组是静态空间,而vector可以动态扩展
3.动态扩展
  • 并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝新空间释放原空间
    C++提高编程(1)
  • vector容器的迭代器是支持随机访问的迭代器

3.2.2vector构造函数

  • 创建vector容器
  • 函数原型
    • 采用模板实现类实现,默认构造函数
    vector<T> v;
    
    • 将v[begin(),end()]区间中的元素拷贝给本身
    vector(v.begin(),v.end())
    
    • 构造函数将n个elem拷贝给本身
    vector(n.elem)
    
    • 拷贝构造函数
    vector(const vector &vec)
    
#include<iostream>
using namespace std;
#include<vector>


void printVector(vector<int> &vec)
{
	for (vector<int>::iterator it = vec.begin();it != vec.end();it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
void test1()
{
	//默认无参构造函数
	vector<int> v1;
	for (int i = 0;i < 10;i++)
	{
		v1.push_back(i + 1);
	}
	printVector(v1);

	// 通过区间方式进行构造
	vector<int> v2(v1.begin(), v1.end());

	printVector(v2);

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

	//拷贝构造
	vector<int> v4(v3);
	printVector(v4);
}


int main()
{
	test1();
	system("pause");
	return 0;
}

3.2.3vector赋值操作

  • 给vector容器赋值操作
  • 函数原型
    • 重载等号操作符
    vector& operator=(const vector &vec)
    
    • 将[begin,end)区间中的数据拷贝赋值给本身
    assign(beg,end)
    
    • 将n个element拷贝赋值给本身
    assign(n,elem)
    
	//赋值 operator=
	vector<int> v11;
	v11 = v4;
	printVector(v11);

	//assign
	vector<int> v12;
	v12.assign(v4.begin(), v4.end());
	printVector(v12);

3.2.4vector容量和大小

  • 对vector容器的容量和大小操作
  • 函数原型
    • 判断容器是否为空
    empty()
    
    • 容器容量
    capacity()
    
    • 返回容器中元素的个数
    size()
    
    • 重新指定容器的长度为num,若容器长度变长,则以默认值填充新位置;如果容器变短,则末尾超出容器长度的元素被删除
    resize(int num)
    
    • 重新指定容器的长度num,若容器变长,则elem值填充新位置;如果容器变短,则末尾超出容器长度的元素被删除
    resize(int num,elem)
    
	cout << "容量:" << v12.capacity() << endl;// 4
	cout << "容量:" << v12.size() << endl;// 4
	v12.resize(8);
	cout << "容量:" << v12.capacity() << endl;// 8
	cout << "容量:" << v12.size() << endl;// 8

3.2.5vector插入和删除

  • 对vector容器进行插入、删除操作
  • 函数原型
    • 尾部插入元素ele
    push_back(ele)
    
    • 删除最后一个元素
    pop_back()
    
    • 迭代器指向位置pos插入元素ele
    insert(const_iterator pos,ele)
    
    • 迭代器指向位置pos插入count个元素ele
    insert(const_iterator pos,int count,ele)
    
    • 删除迭代器指向的元素
    erase(const_iterator pos)
    
    • 删除迭代器从start到end之间的元素
    erase(const_iterator start,const_iterator end)
    
    • 删除容器中所有元素
    claer()
    
	v12.push_back(78);
	cout << "容量:" << v12.capacity() << endl;// 12
	cout << "容量:" << v12.size() << endl;// 9
	printVector(v12);
	//插入迭代器  
	v12.insert(v12.begin(), 10);
	printVector(v12);

3.2.6vector数据存取

  • 对vector中的数据存取操作
  • 函数原型
    • 返回索引idx所指的数据
    at(int idx)
    
    • 返回索引idx所指的数据
    operator[idx]
    
    • 返回容器中第一个数据元素
    front()
    
    • 返回容器中最后一个数据元素
    back()
    

3.2.7vector互换容器

  • 实现两个容器内元素进行互换
  • 函数原型
    • 将vec与本身的元素互换
    swap(vec)
    
//巧用swap收缩内存
v.resize(3);
vector<int>(v).swap(v)

3.2.8vector预留空间

  • 减少vector在动态扩展容量时的扩展次数
  • 函数原型
    • 容器预留len个元素长度,预留位置不初始化,元素不可访问
    reserve(int len)
    

3.3deque容器

3.3.1deque容器基本概念

  • 双端数组,可以对头端进行插入删除操作
  • deque与vector区别
    • vector对于头部的插入删除效率低,数据量越大,效率越低
    • deque相对而言,对头部的插入删除速度会比vector快
    • vector访问元素时的速度会比deque快,这和两者内部实现有关

C++提高编程(1)

  • deque内部工作原理
    • deque内部有一个中控器,维护每段缓冲区中的内容,缓冲区中存放的真实数据
    • 中控器维护的每个缓冲区的地址,使得使用deque时像一片连续的内存空间

C++提高编程(1)

  • deque容器的迭代器也是支持随机访问的

3.3.2deque构造函数

  • deque容器构造
  • 函数原型
    • 默认构造形式
    deque<T> depT
    
    • 构造函数将[beg,end)区间中的元素拷贝给本身
    deque(beg,end)
    
    • 构造函数将n个elem拷贝给本身
    deque(n,elem)
    
    • 拷贝构造函数
    deque(const deque &dep)
    
	#include<iostream>
#include<deque>
using namespace std;

//添加const,避免值被修改
void printDeque(const deque<int> &dep)
{
	for (deque<int>::const_iterator it = dep.begin();it != dep.end();it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}


void test1()
{
	//默认构造
	deque<int> dep;
	for(int i = 0;i< 10;i++)
	{
		dep.push_back(i + 1);
	}
	printDeque(dep);

	deque<int> dep2(dep.begin(), dep.end());
	printDeque(dep2);
}



int main()
{
	test1();
	system("pause");
	return 0;
}

3.3.3deque赋值操作

  • 给deque容器进行赋值
  • 函数原型
    • 重载等号操作符
    deque& operato=(const deque &dep)
    
    • 将[beg,end)区间中的数据拷贝赋值给本身
    assign(beg,end)
    
    • 将n个elem拷贝赋值给本身
    assign(n,elem)
    

3.3.4deque大小操作

  • 对deque容器大小进行操作
  • 函数原型
    • 判断容器是否为空
    empty()
    
    • 返回容器中元素的个数
    size()
    
    • 重新指定容器的长度为num,若容器变长,则以默认值填充新位置,如果容器变短,则末尾超出容器长度的元素被删除
    resize(num)
    
    • 重新指定容器的长度为num,若容器变长,则以elem值填充新位置,如果容器变短,则末尾超出容器长度的元素被删除
    resize(num.elem)
    

3.3.5deque插入删除

  • 向deque容器中插入删除元素
  • 函数原型
    • 容器尾部添加一个元素
    push_back(elem)
    
    • 在容器头部插入一个元素
    push_front(elem)
    
    • 删除容器最后一个元素
    pop_back()
    
    • 删除容器第一个元素
    pop_front()
    
    • 在pos位置插入一个elem元素的拷贝,返回新数据的位置
    insert(const_iterator pos,elem)
    
    • 在pos位置插入n个elem数据,无返回值
    insert(const_iterator pos,n,elem)
    
    • 在pos位置插入[beg,end)区间的数据,无返回值
    insert(const_iterator pos,beg,end)
    
    • 清空容器的所有数据
    clear()
    
    • 删除[beg,end)区间的数据,返回下一个数据的位置
    erase(beg,end)
    
    • 删除pos位置的数据,返回下一个数据的位置
    erase(const_iterator pos)
    
//  在dep1前面插入dep2
dep1.insert(dep1.begin(),dep2.begin(),dep2.end())

3.3.6deque数据存取

  • 对deque中的数据存取操作
  • 函数原型
    • 返回所有idx所指的数据
    at(idx)
    
    • 返回索引idx所指的数据
    operator[]
    
    • 返回容器中第一数据元素
    front()
    
    • 返回容器中最后一个数据元素
    back()
    

3.3.7deque排序

  • 利用算法实现对deque容器进行排序
  • 算法
    • 对beg和end区间内元素进行排序
    sort(iterator beg,iterator end)
    

3.4案例

3.5stack容器

3.5.1stack基本概念

  • stack是一种先进后出(First In Last Out,FILO)的数据结构,它只有一个出口
    C++提高编程(1)
  • 栈中只有顶端的元素才可以被外界使用,因此栈不允许有遍历行为
  • 栈中进入数据称为 ------- 入栈 push
  • 栈中弹出数据称为 ------- 出栈 pop

3.5.3stack常用接口

1.构造函数
  • stack采用模板类实现,stack对象默认构造函数
stack<T> stk;
  • 拷贝构造函数
stack(const stack& stack)
2.赋值操作
  • 重载等号操
stack& operator=(const stack& stack)
3.数据存取
  • 向栈顶添加元素
push(elem)
  • 从栈顶移除第一个元素
pop()
  • 返回栈顶元素
top()
4.大小操作
  • 判断堆栈是否为空
empty()
  • 返回栈的大小
size()

3.6queue容器

3.6.1queue基本概念

  • queue是一种先进先出(First In First Out,FIFO)的数据结构,它有两个出口
    C++提高编程(1)

  • 队列容器允许从一端新增数据,另一端移除数据

  • 队列中只有队头和队尾可以被外界使用,因此队列不允许有遍历行为

  • 队列中进数据称为 — 入队 push

  • 队列中出数据称为 — 出队 pop

3.6.2queue常用接口

  • 栈容器常用的对外接口
1.构造函数
  • queue采用模板类实现,queue对象的默认构造形式
queue<T> que;
  • 拷贝函数
queue(const queue &que)
2.赋值操作
  • 重载等号操作符
queue& operator=(const queue &que)
3.数据存取
  • 往队尾添加元素
push(elem)
  • 从队头移除第一个元素
pop()
  • 返回最后一个元素
back()
  • 返回第一个元素
front()
4.大小操作
  • 判断堆栈是否为空
empty()
  • 返回栈的大小
size()

3.7list容器

3.7.1list基本概念

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

C++提高编程(1)

  • 链表的存储方式并不是连续的内存空间,链表list中的迭代器只支持前移和后移,属于双向迭代器
  • list的优点
    • 采用动态存储分配,不会造成内存浪费和溢出
    • 链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素
  • list的缺点
    • 链表灵活,但是空间(指针域)和时间(遍历)额外消耗较大

3.7.2list构造函数

  • 创建list容器
  • 函数原型
    • list采用模板类实现,对象默认构造形式
    list<T> lst;
    
    • 构造函数将[beg,end)区间中的元素拷贝给本身
    list(beg,end)
    
    • 构造函数将n个elem拷贝给本身
    list(n,elem)
    
    • 拷贝构造函数
    list(const list &lst)
    
#include<iostream>
#include<list>
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 test1()
{
	list<int> lst;
	lst.push_back(20);
	lst.push_back(22);
	lst.push_back(21);
	lst.push_back(29);
	lst.push_back(90);
	printList(lst);

	list<int> lst1(lst.begin(), lst.end());
	printList(lst1);

	list<int> lst2(5, 30);
	printList(lst2);

	list<int> lst3;
	lst3 = lst;
	printList(lst3);
}

int main() 
{
	test1();
	system("pause");
	return 0;
}

3.7.3list赋值和交换

  • 给list容器进行赋值,以及交换list容器
  • 函数原型
    • 将[beg,end)区间中的数据拷贝赋值给本身
    assign(beg,end)
    
    • 将n个elem拷贝给本身
    assign(e,elem)
    
    • 重载等号操作符
    list& operator=(const list &lst)
    
    • 将lst与本身的元素互换
    swap(lst)
    
	list<int> lst4;
	lst4.assign(lst.begin(), lst.end());
	printList(lst4);

	lst4.swap(lst2);
	printList(lst2);
	printList(lst4);

3.7.4list大小操作

  • 对list容器的大小操作
  • 函数原型
    • 返回容器中元素的个数
    size()
    
    • 判断容器是否为空
    empty()
    
    • 重新指定容器的长度为num,若容器变长,则以默认值填充新位置;如果容器变短,则末尾超出容器长度的元素被删除
    resize(num)
    
    • 重新指定容器的长度为num,若容器变长,则以elem值填充新位置;如果容器变短,则末尾超出容器长度的元素被删除
    resize(num,elem)
    

3.7.5list插入和删除

  • 对list容器进行数据的插入和删除
  • 函数原型
    • 在容器尾部加入一个元素
    push_back(elem)
    
    • 删除容器最后一个元素
    pop_back()
    
    • 在容器开头插入一个元素
    push_front(elem)
    
    • 从容器开头移除第一个元素
    pop_front()
    
    • 在pos位置插入elem元素的拷贝,返回新数据的位置
    insert(pos,elem)
    
    • 在pos位置插入n个elem数据,无返回值
    insert(pos,n,elem)
    
    • 在pos位置插入[beg,end)区间的数据,无返回值
    insert(pos,beg,end)
    
    • 移除容器的所有数据
    clear()
    
    • 删除[beg,end)区间的数据,返回下一个数据的位置
    erase(beg,end)
    
    • 删除pos位置的数据,返回下一个数据的位置
    erase(pos)
    
    • 删除容器中所有与elem值匹配的元素
    remove(elem)
    
	lst4.insert(lst4.begin(), 4, 22);
	printList(lst4);
	lst4.pop_back();
	printList(lst4);

3.7.6list数据存取

  • 对list容器中数据进行存取(不支持随机访问)
  • 函数原型
    • 返回第一个元素
    front()
    
    • 返回最后一个元素
    back()
    
//验证迭代器是否支持随机访问
list<int>::iterator it = lst.begin();
it++;
it--;
// it = it + 1;//error,不支持随机访问

3.7.7list反转和排序

  • 将容器中的元素反转,以及将容器中的数据进行排序
  • 函数原型
    • 反转链表
    reverse()
    
    • 链表排序
    sort()
    
  • 所有不支持随机访问迭代器的容器,不支持用标准算法
lst.sort()

3.8set/multiset容器

3.8.1set基本概念

1.简介
  • 所有元素都会在插入时自动被排序
2.本质
  • set/multiset属于关联式容器,底层结构是用二叉树实现
3.set和multiset区别
  • set不允许容器存中有重复的元素
  • multiset允许容器中有重复的元素
#include<iostream>
using namespace std;
#include<set>

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

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

	cout << "遍历容器" << endl;
	printSet(st);// 10 20 30 40 50
}



int main() 
{
	test();
	system("pause");
	return 0;
}

3.8.2set构造和赋值

  • 创建set容器以及赋值
  • 函数原型
    • 构造
      • 默认构造函数
      set<T> st
      
      • 拷贝构造函数
      set(const set &st)
      
    • 赋值
      • 重载等号操作符
      set& operator=(const set &st)
      

3.8.3set大小和交换

  • 统计set容器大小以及交换set容器
  • 函数原型
    • 返回容器中元素的数据
    size()
    
    • 判断容器是否为空
    empty()
    
    • 交换两个集合容器
    swap()
    

3.8.4set插入和删除

  • set容器进行插入数据删除数据
  • 函数原型
    • 在容器中插入元素
    insert(elem)
    
    • 清除所有元素
    clear()
    
    • 删除pos迭代器所指元素,返回下一个元素的迭代器
    erase(pos)
    
    • 删除区间[beg,end)的所有元素,返回下一个元素的迭代器
    erase(beg,end)
    
    • 删除容器中值为elem的元素
    erase(elem)
    
set<int>::iterator it = st.begin();
it++;
set<int>::iterator its = st.erase(it);
cout << "下一个元素的迭代器:" << *its << endl;// 30

3.8.5set查找和统计

  • 对set容器进行查找数据以及统计
  • 函数原型
    • 查找key是否存在,若存在,返回该键的元素的迭代器,若不存在,返回set.end()
    find(key)
    
    • 统计key的元素个数
    count(key)
    
set<int>::iterator it1= st.find(20);
	if (it1 != st.end())
	{
		cout << "找到元素:" << *it1 << endl;
	}
	else
	{
		cout << "未找到该元素" << endl;
	}
	int num = st.count(30);
	cout << "元素的个数:" << num << endl;// 1

3.8.6set和multiset区别

  • 区别
    • set不可以插入重复数据,而multiset
    • set插入数据的同时会返回插入结果,表示是否插入成功
    • multiset不会检测数据,因此可以插入重复数据

3.8.7pair对组创建

  • 成对出现的数据,利用对组可以返回两个数据
  • 两种创建方式
    pair<type,type> p (value1,value2)
    pair<type,type> p = make_pair(value1,value2)
    
#include<iostream>
using namespace std;


void test()
{
	//第一种方式
	pair<string, int> p("Tom", 22);
	cout << "姓名:" << p.first << ",年龄:" << p.second << endl;

	//第二种方式
	pair<string, int> p2 = make_pair("Jerry", 18);
	cout << "姓名:" << p2.first << ",年龄:" << p2.second << endl;
}

int main()
{
	test();
	system("pause");
	return 0;
}

3.8.8set容器排序

  • set容器默认排序规则为从小到大,改变排序规则
  • 技术点
    • 利用仿函数,可以改变排序规则
#include<iostream>
#include<deque>
#include<algorithm>
using namespace std;

//添加const,避免值被修改
void printDeque(const deque<int> &dep)
{
	for (deque<int>::const_iterator it = dep.begin();it != dep.end();it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}


void test1()
{
	//默认构造
	deque<int> dep;
	for(int i = 0;i< 10;i++)
	{
		dep.push_back(i + 1);
	}
	printDeque(dep);

	deque<int> dep2(dep.begin(), dep.end());
	printDeque(dep2);
	sort(dep2.begin(), dep2.end());
	printDeque(dep2);
}



int main()
{
	test1();
	system("pause");
	return 0;
}

3.9map/multimap容器

3.9.1map基本概念

1.简介
  • map中所有元素都是pair
  • pair中第一个元素为key(键值),起索引作用;第二个元素为value(实值)
  • 所有元素都会根据元素的键值自动排序
2.本质
  • map/multimap属于关联式容器,底层结构使用二叉树实现
3.优点
  • 可以根据key值快速找到value值
4.map和multimap区别
  • map不允许容器中重复key值元素
  • multimap允许容器中重复key值元素

3.9.2map构造和赋值

  • 对map容器进行构造和赋值操作
  • 函数原型
    • ma默认构造函数
    map<T1,T2> mp;
    
    • 拷贝构造函数
    map(const map &mp)
    
    • 重载等号操作符
    map &operator=(const map& mp)
    
map<string,int> mp;
mp.insert(pair<string,int> ("Tom",18));

3.9.3map大小和交换

  • 统计map容器的大小以及交换map容器
  • 函数原型
    • 返回容器中元素的数目
    size()
    
    • 判断容器是否为空
    empty()
    
    • 交换两个集合
    swap()
    

3.9.4插入和删除

  • map容器进行插入数据和删除数据
  • 函数原型
    • 在容器中插入元素
    insert(elem)
    
    • 清除所有元素
    clear()
    
    • 删除pos迭代器所指的元素,返回下一个元素的迭代器
    erase(pos)
    
    • 删除区间[beg,end)的所有元素,返回下一个元素的迭代器
    erase(beg,end)
    
    • 删除容器中值为key的元素
    erase(key)
    

3.9.5map查找和统计

  • 对map容器进行查找数据以及统计数据
  • 函数原型
    • 查找key是否存在,若存在,返回该键的元素的迭代器,若不存在,返回set.end()
    find(key)
    
    • 统计key的元素个数
    count(key)
    

3.9.6map容器排序

  • map容器默认排序规则为按照key值进行 从小到大排序
  • 技术点
    • 利用仿函数,可以改变排序规则

#include<iostream>
using namespace std;
#include<map>
#include<string>

class MyCompare 
{
public:
	bool operator()(string str1,string str2)const
	{
		return str1 > str2;
	}
};

void test()
{
	map<string, int,MyCompare> mp;
	//mp.insert(pair<string, int>("数字化", 13));
	mp.insert(make_pair("数字化", 13));//同上
	mp.insert(make_pair("模块化", 31));
	mp.insert(make_pair("智能化", 20));
	mp.insert(make_pair("系统化", 8));
	mp.insert(make_pair("助手化", 22));
	for (map<string, int>::iterator it = mp.begin();it != mp.end();it++)
	{
		cout << "项目名称: " << (*it).first << ",项目年限:" << it->second << endl;
	}
}

int main()
{
	test();
	system("pause");
	return 0;
}