4. 模板 STL容器


黑马程序员课程学习笔记

模板

template<class T>
//接着写的是类,就叫类模板,如果是函数,就是函数模板

学习模板不是为了写模板,而是在STL中会用系统提供的模板。
有函数模板和类模板,声明时都可以用class

template<typename T>或者template<class T>

模板函数

基本用法

语法:
//告诉编译器,要声明一个函数模板,后面的T不要报错,T是任意数据类型
template<typename T>//或者template<class T>
//紧跟的第一个函数就是模板函数
void mySwap(T &a,T &b);

调用
//第一种是指定数据类型
mySwap<int>(a, b);
//第二种是编译器自己推导
mySwap(a, b);

//a和b必须是一致的数据类型,如果a是int,b是char,就不能交换,指定数据类型也不行
mySwap<int,char>(a, c);//错误示范

如果定义了一个模板函数,但是里面没有T,调用的时候也必须指定数据类型
template<typename T>
void func()
{
	cout<<"func"<<endl;
}
//调用,也得指定一个数据类型
func<int>();

注意事项:

  1. 自动类型推导,必须推导出一致的数据类型T,才能使用
  2. 模板必须确定出T的数据类型,才能使用
  3. 函数模板和类模板都可以用class,效果一样

比如要交换两个数,如果每个数据类型都写一个太麻烦了,写个通用的,调用的时候告诉再指定相应的数据类型就可以了,甚至都不用告诉,让编译器自己推导。

#include <iostream>
using namespace std;

//函数模板

//这样交换,数据类型不计其数,但是很多相似之处,通过模板能实现任意类型的数据交换

//交换两个整型
void swapInt(int &a,int &b) 
{
	int temp = a;
	a = b;
	b = temp;
}

//交换两个浮点型
void swapDouble(double& a, double& b)
{
	double temp = a;
	a = b;
	b = temp;
}

void test01() 
{
	int a = 10;
	int b = 20;

	swapInt(a, b);
	cout << "a=" << a << endl;
	cout << "b=" << b << endl;

	double c = 1.1;
	double d = 2.2;
	swapDouble(c, d);
	cout << "c=" << c << endl;
	cout << "d=" << d << endl;
	
}

//函数模板

//告诉编译器,要声明一个函数模板,后面的T不要报错,T是任意数据类型
template<typename T>

//做一个交换函数
void mySwap(T &a,T &b) 
{
	T temp = a;
	a = b;
	b = temp;
}

void test02()
{
	int a = 10;
	int b = 20;
	//1.直接传入模板函数,编译器推导出T代表的数据类型
	mySwap(a, b);

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


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

}

int main() {

	//test01();
	test02();

	system("pause");
	return 0;
}

局限性

内置的数据类型可以操作,但是数组,或者自定义的类,无法直接操作。这时候可以用之前的运算符重载操作,也可以对模板函数关于具体的类进行重载

#include <iostream>
using namespace std;

class Person {

public:
	string m_Name;
	int m_Age;
};

template<class 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 test01() 
{
	int a = 1, b = 2;
	bool ret = myCompare(a, b);
	cout << "ret=" << ret << endl;
}

//但是对于Person数据类型不能直接操作
void test02()
{
	Person p1 = { "张三",22 };
	Person p2 = { "张三",22 };

	bool ret=myCompare<Person>(p1, p2);

	if (ret)
	{
		cout << "相等" << endl;
	}
	else
	{
		cout << "不相等" << endl;
	}
}

int main() {

	//test01();
	test02();

	system("pause");
	return 0;
}

类模板

基本用法

//template<class T>
//接着写的是类,就叫类模板,如果是函数,就是函数模板

注意事项:
1. 不能推导,只能指定
2. 模板参数列表可以有默认值,加上默认值之后,调用时有指定就用指定的,没有指定的就用默认的。

类外实现注意事项:
1.类模板声明
2.作用域名<T>::
3.2里面的T和上面类定义一致

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

//template<class T>
接着写的是类,就叫类模板,如果是函数,就是函数模板

//但是Person有姓名和年龄,单独一个T是不够的
template<class NameType,class AgeType>
class Person {

public:

	//构造函数
	Person(NameType name, AgeType age);
	//{
	//	this->m_Name = name;
	//	this->m_Age = age;
	//}

	void showInfo()
	{
		cout << "姓名:" << this->m_Name << "\t年龄:" << this->m_Age << endl;
	}

	NameType m_Name;
	AgeType m_Age;
};

/// <summary>
/// 类外实现,要加三个东西
/// 1.类模板声明
/// 2.作用域名<T>::
/// 3.2里面的T和上面类定义一致
/// </summary>

template<class NameType, class AgeType>
Person<NameType, AgeType>::Person(NameType name, AgeType age)
{
	this->m_Name = name;
	this->m_Age = age;
}

void test01()
{
	//分别指定数据类型
	Person <string,int> p1("张三", 22);

	p1.showInfo();
}

int main() {

	test01();

	system("pause");
	return 0;
}

类模板成员函数创建时机

#include <iostream>
using namespace std;

//先创建两个类
class Person1 {

public:
	//成员函数
	void showPerson1() 
	{
		cout << "Person1" << endl;
	}
};


class Person2 {

public:
	//成员函数
	void showPerson2() 
	{
		cout << "Person2" << endl;
	}
};

//创建一个类模板
template<class T>
class myClass {

public:
	//成员变量,数据类型T仍未确定
	T obj;

	//成员函数
	void func1() {
		obj.showPerson1();
	}
	void func2() {
		obj.showPerson2();
	}
};

void test01()
{
	//这里给了数据类型
	myClass<Person1>m;

	//开始调用,Person1数据类型,只能调用func1,不能func2,
	//但是myClass里面没有报错
	m.func1();
	//m.func2();
}

int main() {

	test01();

	system("pause");
	return 0;
}

类模板对象作函数参数

利用typeid(typename).name()来打印编译器推导出来的数据类型

共有三种方式,1比较好用

  1. 指定传入类型,把参数类型都告诉编译器
  2. 参数模板化:让编译器自己推导参数模板
  3. 整个类模板化:把整个类作为一个T
  4. 利用typeid(typename).name()来打印编译器推导出来的数据类型
#include <iostream>
using namespace std;


//利用typeid(typename).name()来打印编译器推导出来的数据类型

template<class nameType, class ageType>
class Person {

public:

	Person(nameType name,ageType age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}

	void showInfo()
	{
		cout << "姓名:" << this->m_Name << "\t年龄:" << this->m_Age << endl;
	}

	nameType m_Name;
	ageType m_Age;
};

//三种传入方式

//1.指定传入类型
void showPerson1(Person<string, int> &p)
{
	p.showInfo();
}

//2.参数模板化:将对象中的参数变为模板
template<class nameType, class ageType>
void showPerson2(Person<nameType, ageType>& p)
{
	p.showInfo();

	//cout << "nameType的数据类型" << typeid(nameType).name() << endl;
	//cout << "ageType的数据类型" << typeid(ageType).name() << endl;
}

//3.整个类模板化
template<class T>
void showPerson3(T& p)
{
	p.showInfo();
	cout << "T的数据类型" << typeid(T).name() << endl;
}

//调用 
void test01()
{
	//1.传入指定的数据类型
	Person<string, int>p1("张三", 23);
	showPerson1(p1);

	//2.让编译器推导参数模板
	Person<string, int>p2("李四", 23);
	showPerson2(p2);

	//3.让编译器推导整个类模板
	Person<string, int>p3("王五", 23);
	showPerson3(p3);
	
}

int main() {

	test01();

	system("pause");
	return 0;
}

类模板继承

父类是一个类模板,子类继承时必须指定父类的数据类型。如果不想一下子把数据类型给出,则把子类也形成一个模板函数,并用模板代替父类数据类型。

#include <iostream>
using namespace std;

template<class T>
class Base {

public:
	T m;

	void fun()
	{
		cout << "T的数据类型" << typeid(T).name() << endl;
	}
};

//要继承,就必须指定T的数据类型
class Son :public Base<int> {

};

//这样写更灵活,没有把int定死了,而是用的模板,调用时,子类指定父类数据类型
template<class T1,class T2>
class Son2 :public Base<T2>
{
public:
	T2 obj;
	void baseFun()
	{
		cout << "T1的数据类型:" << typeid(T1).name() << endl;
		cout << "T2的数据类型:" << typeid(T2).name() << endl;
	}
	
};

void test01()
{
	Son s1;
}

void test02()
{
	//这里int和char对应定义的T1,T2
	//T2给了父类
	Son2<int,char> s2;
	s2.fun();
	s2.baseFun();
	
}

int main() {

	//test01();
	test02();

	return 0;
}

类模板分文件编写

由于类模板中成员函数在调用时才创建,所以分文件编写不同于之前普通的类。
有两种方式可以解决这一问题,推荐方式2

  1. .h和.cpp还是分开写,调用时只包含.cpp,而不包含.h
  2. .h和.cpp不要分开写,就写在一个文件里面,命名为.hpp,这是约定俗成的命名方式

Person.hpp,调用时直接包含该hpp文件

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

using namespace std;

template<class T1, class T2>
class Person {

public:

	//构造函数
	Person(T1 name, T2 age);

	//成员函数,显示信息
	void showInfo();

	//成员变量
	T1 m_Name;
	T2 m_Age;
};

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

//成员函数,显示信息
template<class T1, class T2>
void Person<T1, T2>::showInfo()
{
	cout << "姓名:" << this->m_Name << "\t年龄:" << this->m_Age << endl;
}

类模板与友元

全局函数的类内实现简单,类外实现非常复杂,主要有以下几点:

  1. friend void outShowInfo<>(Person<T1, T2>& p);//<>很关键
  2. 类的声明,全局函数类外实现,类的实现,有顺序要求,否则编译器不认识
#include <iostream>
#include <string>

using namespace std;

//函数里面有类,要写在函数实现前面
template<class T1, class T2>
class Person;

//类外实现要写在类的前面,让编译器知道有这样的实现
//全局函数显示信息,类外实现
template<class T1, class T2>
//由于不是成员函数,不需要作用域
void outShowInfo(Person<T1, T2>& p)
{
	cout << "姓名:" << p.m_Name << "\t年龄:" << p.m_Age << endl;
}

template<class T1, class T2>
class Person {

public:

	//构造函数
	Person(T1 name, T2 age);

	//全局函数显示信息,类内实现
	friend void inShowInfo(Person<T1,T2>& p)
	{
		cout << "姓名:" << p.m_Name << "\t年龄:" << p.m_Age << endl;
	}

	//全局函数显示信息,类内声明
	//要是没有<>,类外实现的时候,是一个模板函数,而非全局函数,会报错
	friend void outShowInfo<>(Person<T1, T2>& p);

private:
	//成员变量
	T1 m_Name;
	T2 m_Age;
};

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


void test01()
{
	Person<string,int> p1("张三",22);
	inShowInfo(p1);
}

void test02()
{
	Person<string, int> p2("李四", 22);
	outShowInfo(p2);
}



int main() {

	//test01();
	test02();

	system("pause");
	return 0;
}

类模板案例: 深拷贝、运算符重载

自定义一个数组的类模板,可以存放内置数据类型,也可以存放自定义数据类型,并且可以对数组进行增加或删除元素的操作。以下案例有三个文件,注意深拷贝与string的加法

1.自定义数组类模板:myArray.hpp

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

//自己的通用的数组类

template<class T>
class myArray {

public:
	//构造函数
	myArray(int capacity)
	{
		//cout << "myArray有参构造调用" << endl;
		this->m_Capacity = capacity;
		this->m_Size = 0;
		pAddress = new T[this->m_Capacity];
	}

	//拷贝构造
	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];

		//如果arr中有数据,数据也要拷贝
		if (arr.pAddress!=NULL)
		{
			for (int i = 0; i < arr.m_Size; i++)
			{
				//这里不用再在堆区开辟
				//pAddress[i] = new T[*arr.pAddress[i]];
				//直接相等
				this->pAddress[i] = arr.pAddress[i];
			}
		}
		
	}
	//重载=,防止浅拷贝,为了连等
	myArray & operator=(const myArray& arr)
	{
		//cout << "=重载" << endl;
		//先判断原来堆区是否有数据,有则清空
		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];

		//如果arr中有数据,数据也要拷贝
		if (arr.pAddress != NULL)
		{
			for (int i = 0; i < arr.m_Size; i++)
			{
				//这里不用再在堆区开辟
				//pAddress[i] = new T[*arr.pAddress[i]];
				//直接相等
				this->pAddress[i] = arr.pAddress[i];
			}
		}

		return *this;
	}

	//尾插法
	void Push_Back(const T &val)
	{
		//先判断数组是否到达容量上限
		if (this->m_Size==this->m_Capacity)
		{
			return;
		}

		else
		{
			this->pAddress[this->m_Size] = val;
			//更新数组元素个数
			this->m_Size++;
		}
	}

	//尾删法
	void Pop_Back()
	{
		//元素个数减一,让用户访问不到
		if (this->m_Size==0)
		{
			return;
		}
		this->m_Size--;
	}

	//通过下标访问数组元素,必须重载[]
	//单是一个T可以访问元素,但是想要赋值,还得加&,如arr[0]=1;
	T& operator[](int index)
	{
		return this->pAddress[index];
	}

	//析构函数
	~myArray()
	{
		//cout << "myArray析构调用" << endl;
		//释放堆区数组
		if (this->pAddress!=NULL)
		{
			delete[] this->pAddress;
			this->pAddress = NULL;
		}
	}


	//获取数组容量
	int getCapacity()
	{
		return this->m_Capacity;
	}
	//获取元素个数
	int getSize()
	{
		return this->m_Size;
	}

private:
	//数组名,指向堆区开辟的真实数组
	T* pAddress;
	//容量
	int m_Capacity;
	//数组元素个数
	int m_Size;
};

2.自定义数据类型:Person.hpp

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

using namespace std;

template<class T1, class T2>
class Person {

public:

	Person();

	//构造函数
	Person(T1 name, T2 age);

	//成员函数,显示信息
	void showInfo();

	//成员变量
	T1 m_Name;
	T2 m_Age;
};

//无参构造
template<class T1, class T2>
Person<T1,T2>::Person()
{

}

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

//成员函数,显示信息
template<class T1, class T2>
void Person<T1, T2>::showInfo()
{
	cout << "姓名:" << this->m_Name << "\t年龄:" << this->m_Age << endl;
}

3.测试案例.cpp

#include "myArray.hpp"
#include "Person.hpp"

void test01()
{
	//内置数据类型的测试

	myArray<int> arr1(10);
	for (int i = 0; i < 10; i++)
	{
		arr1.Push_Back(i);
		//cout << "arr1[" << i << "]=" << arr1[i] << endl;
	}
	arr1.Pop_Back();

	myArray <int>arr2(arr1);

	for (int i = 0; i < arr1.getSize(); i++)
	{
		cout << "arr2[" << i << "]=" << arr1[i] << endl;
	}


}

void test03()
{
	//自定义数据类型的测试

	myArray<Person<string, int>>arr(5);

	Person<string, int>p1("张三", 22);
	Person<string, int>p2("张四", 23);
	Person<string, int>p3("张五", 24);
	Person<string, int>p4("张六", 25);
	Person<string, int>p5("张七", 26);

	arr.Push_Back(p1);
	arr.Push_Back(p2);
	arr.Push_Back(p3);
	arr.Push_Back(p4);
	arr.Push_Back(p5);

	for (int i = 0; i < arr.getSize(); i++)
	{
		arr[i].showInfo();
	}
}

void test04()
{
	//最初的想法是这样写,但是有一个问题,arr的元素个数无法更新,必须有push_back

	myArray<Person<string, int>>arr(5);

	string nameSeed = "ABCDE";

	for (int i = 0; i < arr.getCapacity(); i++)
	{
		//名字,注意不能合在一起写,
		arr[i].m_Name = "Person_";
		arr[i].m_Name += nameSeed[i];
		arr[i].m_Age =20+rand()%10;

		//不知道这样写是否合法,但是不用pushback就无法更新数组的元素个数
		//也许再另外构造一个Person数组更好
		arr.Push_Back(arr[i]);

		//arr[i].showInfo();
	}

	// 没有pushback,这里的arr.getSize()=0

	arr.Pop_Back();

	for (int i = 0; i < arr.getSize(); i++)
	{
		arr[i].showInfo();
	}
}

int main() {

	//test01();
	//test03();
	test04();

	system("pause");
	return 0;
}

STL

STL,standard template library,标准模板库。是一套标准,分为容器、算法、迭代器三大部分,其中迭代器连接容器和算法。
迭代器实质是一个指针,指向容器中的数据的地址,解引用后得到该数据

常用容器

string

主要是赋值,查找、替换、删除、读取,截取字串等操作。

构造函数
#include <iostream>
#include <string>
using namespace std;

//string本质是一个类,构造函数原型:

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

void test01()
{
	//1.空字符串
	string s1;
	cout << "s1=" << s1 << endl;

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

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

	//4.使用n个字符c初始化
	string s4(5, '*');
	cout << "s4=" << s4 << endl;
}

int main()
{
	test01();

	system("pause");
	return 0;
}
赋值
  1. 利用=直接赋值
  2. 利用内置成员函数assign
#include <iostream>
#include <string>
using namespace std;

/*
赋值的函数原型:

string& operator=(const char* s);		//char*类型字符串 赋值给当前的字符串
string& operator=(const string& s);		//把字符串s赋给当前的字符串
string& operator=(char c);				//字符赋值给当前的字符串
string& assign(const char* s);			//把字符串s赋给当前的字符串
string& assign(const char* s, int n);	 //把字符串s的前n个字符赋给当前的字符串
string& assign(const string& s);		//把字符串s赋给当前字符串
string& assign(int n, char c);			//用n个字符c赋给当前字符串
*/

void test01()
{
	string s1;
	s1 = "hello world";
	cout << s1 << endl;

	string s2;
	s2 = s1;
	cout << s2 << endl;

	string s3;
	s3 = 'c';
	cout << s3 << endl;

	string s4;
	s4.assign("hello world");
	cout << s4 << endl;

	string s5;
	s5.assign("hello world",5);
	cout << s5 << endl;

	string s6;
	s6.assign("abcde");
	cout << s6 << endl;

	string s7;
	s7.assign("abcde", 3);
	cout << s7 << endl;

	//注意s8和s7区别,s8写法可能不合法
	string s8;
	s8.assign(s6, 3);
	cout << s8 << endl;

	string s9;
	s9.assign(5, 'a');
	cout << s9 << endl;
}

int main()
{
	test01();

	system("pause");
	return 0;
}
拼接
  1. 利用+=
  2. 利用内置成员函数append
#include <iostream>
#include <string>
using namespace std;
/*
string拼接:

string& operator+=(const char* str);                     //重载+=操作符
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& s);                             //同operator+=(const string& str)
string& append(const string& s, int pos, int n);     //字符串s中从pos开始的n个字符连接到字符串结尾

*/
void test01()
{
	//简单来说,两种拼接方式,+=和append
	string s1 = "welcome";
	s1 += " to ";

	string s2 = s1;
	string s3 = s1;
	s1.append("Beijing TianAnMen");
	cout << s1 << endl;

	//前7个
	s2.append("Beijing TianAnMen", 7);
	cout << s2 << endl;
	
	//从第8开始的8个字符,0是第一个
	s3.append("Beijing TianAnMen", 8,8);
	cout << s3 << endl;

}

int main()
{
	test01();

	system("pause");
	return 0;
}
查找替换
  1. find(‘a’,start),可以指定从start开始
  2. rfind,从右往左找,但是返回的下标依旧是从左开始
  3. replace
#include <iostream>
using namespace std;

/*
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;                         //查找字符c最后一次出现位置
string& replace(int pos, int n, const string& str);         //替换从pos开始n个字符为字符串str
string& replace(int pos, int n, const char* s);                   //替换从pos开始的n个字符为字符串s
*/
void test01()
{
	//find
	string s1 = "abcdefg hijklmncd";
	int ret = s1.find("hi");
	if (ret !=-1)
	{
		cout << "位置在:" << ret << endl;
	}
	else
	{
		cout << "未找到字符串" << endl;
	}

	//rfind,从右往左查,但是下标依然是左往右
	int ret2 = s1.rfind("cd");
	if (ret2 != -1)
	{
		cout << "位置在:" << ret2 << endl;
	}
	else
	{
		cout << "未找到字符串" << endl;
	}

}

void test02()
{
	string s1 = "abcdefg hijklmncd";
	//从1号位置起的9个字符换成88
	s1.replace(1, 9, "88");
	cout << s1 << endl;
}

int main() {
	
	//test01();
	test02();

	system("pause");
	return 0;
}
字符串比较

内置成员函数比较两串字符的ASCLL码大小,相等则返回0;

#include <iostream>
#include <string>

using namespace std;

//实质是比较两串字符的ascll
//相等返回0,大于返回1,小于返回-1

void test01()
{
	string str1 = "hello world!!!";
	string str2 = "hello world";

	int ret=str2.compare(str1);
	cout << "ret=" << ret << endl;
}

int main() {

	test01();

	system("pause");
	return 0;
}
字符串存取
  1. []
  2. 内置成员函数.at(i)
#include <iostream>
#include <string>

using namespace std;

/*
string中单个字符存取方式有两种:

char& operator[](int n);//通过[]方式取字符
char& at(int n);		//通过at方法获取字符
*/

void test01()
{
	string str = "hello world!";
	cout << "str=" << str << endl;

	//输出单个字符

	//1.[]
	for (int i = 0; i < str.size(); i++)
	{
		//size是string的成员函数,返回字符串长度
		cout << str[i] << " ";
	}
	cout << endl;

	//2.at
	for (int i = 0; i < str.size(); i++)
	{
		cout << str.at(i) << " ";
	}
	cout << endl;

	//3.修改单个字符
	str[0] = 'A';
	str.at(1) = 'B';
	cout << "str=" << str<<endl;

}

int main() {

	test01();

	system("pause");
	return 0;
}
插入 删除 截取子串

内置成员函数insert、erase与substr

#include <iostream>
#include <string>

using namespace std;

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

void test01()
{
	string str = "hello world";
	cout << "str=" << str << endl;

	str.insert(0, "aaa");
	cout << "str=" << str << endl;

	str.insert(3, 5,'b');
	cout << "str=" << str << endl;

	//从0号位置开始删除8个字符
	str.erase(0, 8);
	cout << "str=" << str << endl;

}

void test02()
{
	//使用操作,保留邮箱用户名
	string email = "zhangsan@qq.com";

	//截取一部分,另存
	string sub_str = email.substr(6, 5);
	cout << "sub_str=" << sub_str << endl;	

	int index = -1;
	//for (int i = 0; i < email.size(); i++)
	//{
	//	if (email[i]=='@')
	//	{
	//		index = i;
	//	}
	//}
	//直接用find
	index = email.find('@');
	string name = email.substr(0, index);
	cout << "用户名:" << name << endl;
}

int main() {

	//test01();
	test02();

	system("pause");
	return 0;
}

vector

与数组非常相似,但是数组的容量在定义时已经规定,不能扩展,但是vecotr可以动态扩展。
动态扩展:不是在原空间后面接着写,而是另找一块更大的空间,把原数据拷过来,释放原空间。
vector容器前面封闭,后面开放,数据的存入也是尾插,要想插在前面,数据得整体后移,如此一来效率低。

构造函数
#include <iostream>
#include <vector>
using namespace std;

/*
* vector 构造函数
* 
vector<T> v;               		   //采用模板实现类实现,默认构造函数
vector(v.begin(), v.end());        //将v[begin(), end())区间中的元素拷贝给本身。
vector(n, elem);                   //构造函数将n个elem拷贝给本身。
vector(const vector &vec);         //拷贝构造函数。
*/

//一个输出函数
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 < 5; i++)
	{
		v1.push_back(i);
	}
	printVector(v1);

	//通过区间的方式
	vector<int> v2(v1.begin(), v1.end());
	printVector(v2);

	//n个elem赋值
	vector<int> v3(5, 10);
	printVector(v3);

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

int main() {

	test01();

	system("pause");
	return 0;
}
赋值
#include <iostream>
#include <vector>
using namespace std;

/*
* 赋值操作,与构造有些类似
* 
vector& operator=(const vector &vec);//重载等号操作符
assign(beg, end); //将[beg, end)区间中的数据拷贝赋值给本身。
assign(n, elem); //将n个elem拷贝赋值给本身。
*/

//一个输出函数
void printVector(vector<int>& v)
{
	for (vector<int>::iterator it = v.begin(); it < v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

void test01()
{
	//先构造一个v1
	vector<int> v1;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}
	printVector(v1);

	//等号赋值
	vector<int> v2 = v1;
	printVector(v2);

	//区间赋值
	vector<int> v3;
	v3.assign(v1.begin(), v1.end());
	printVector(v3);

	//10个0赋值
	vector<int> v4;
	v4.assign(10, 0);
	printVector(v4);



}

int main() {

	test01();

	system("pause");
	return 0;
}
容量和大小
#include <iostream>
#include <vector>
using namespace std;

/*
empty();                     //判断容器是否为空

capacity();                 //容器的容量

size();                     //返回容器中元素的个数

resize(int num);           //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。

  ​					      //如果容器变短,则末尾超出容器长度的元素被删除。

resize(int num, elem);    //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。

  ​				          //如果容器变短,则末尾超出容器长度的元素被删除
*/

//一个输出函数
void printVector(vector<int>& v)
{
	for (vector<int>::iterator it = v.begin(); it < v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

void test01()
{
	//先构造一个v1
	vector<int> v1;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}
	printVector(v1);

	if (v1.empty())
	{
		cout << "v1为空" << endl;
	}
	else
	{
		cout << "v1不为空" << endl;
		//v1里面只有10个数,也没有设置初始容量,自动扩容了
		cout << "v1的容量:" << v1.capacity() << endl;
		cout << "v1的大小:" << v1.size() << endl;

		//尾插之后,仍继续扩容

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

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

	//改变容量,扩大了则补0或指定的elem;
	//减少了则裁去超出的尾部

	v1.resize(15);
	printVector(v1);
	cout << "v1的容量:" << v1.capacity() << endl;
	cout << "v1的大小:" << v1.size() << endl;

	v1.resize(20, 9);
	printVector(v1);
	cout << "v1的容量:" << v1.capacity() << endl;
	cout << "v1的大小:" << v1.size() << endl;

}

int main() {

	test01();

	system("pause");
	return 0;
}
插入 删除
#include <iostream>
#include <vector>
using namespace std;

/*
push_back(ele);//尾部插入元素ele
pop_back();//删除最后一个元素
insert(const_iterator pos, ele);//迭代器指向位置pos插入元素ele
insert(const_iterator pos, int count,ele);//迭代器指向位置pos插入count个元素ele
erase(const_iterator pos);                     //删除迭代器指向的元素
erase(const_iterator start, const_iterator end);//删除迭代器从start到end之间的元素
clear();                                                       //删除容器中所有元素
*/

//一个输出函数
void printVector(vector<int>& v)
{
	for (vector<int>::iterator it = v.begin(); it < v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

void test01()
{
	//先构造一个v1
	vector<int> v1;
	for (int i = 0; i < 5; i++)
	{
		v1.push_back(i);
	}
	printVector(v1);

	//尾删法,删除最后一个
	v1.pop_back();
	printVector(v1);

	//插入,指定位置插入元素,(pos,ele);
	v1.insert(v1.begin() + 1, 10);
	printVector(v1);

	//插入,指定位置插入n个元素,(pos,n,ele);
	v1.insert(v1.begin() + 3, 3,20);
	printVector(v1);

	//删除指定位置元素
	v1.erase(v1.begin() + 1);
	printVector(v1);

	//删除指定区间元素,这里删了3个数
	v1.erase(v1.begin()+2 , v1.begin() + 5);
	printVector(v1);

	//clear
	v1.clear();
	printVector(v1);

}

int main() {

	test01();

	system("pause");
	return 0;
}
数据读取

不使用迭代器,通过简单的[]和at函数也能访问到数据

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

//不用迭代器,使用[]和.at()读取数据
//front()和back(),读取第一个和最后一个元素

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

	for (int i = 0; i < v.size(); i++)
	{
		//1.[]
		cout << v[i] << " ";

		//2. .at(i)
		//cout << v.at(i)<<" ";
	}
	cout << endl;

	//获取首个数据
	cout << "第一个数据:" << v.front() << endl;;
	//获取尾部数据
	cout << "最后一个数据:" << v.back() << endl;;
}

int main() {

	test01();

	system("pause");
	return 0;
}

容器互换

实际用途之一:缩小容器容量,节省空间。如一个很大的容器,里面只装了几个数据,浪费空间。通过匿名容器交换,可以缩减容量,节省空间。

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

//利用swap交换元素


//一个输出函数
void printVector(vector<int> &v)
{
	for (vector<int>::iterator it=v.begin();it<v.end();it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

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


	vector<int> v2;

	for (int i = 9; i >= 0; i--)
	{
		v2.push_back(i);
	}
	printVector(v2);

	cout << "交换后:" << endl;
	v1.swap(v2);
	printVector(v1);
	printVector(v2);	
	
}

//实用操作,缩小容器容量
void test02()
{
	vector<int> v;
	for (int i = 0; i < 10000; i++)
	{
		v.push_back(i);
	}
	cout << "初始:" << endl;
	cout << "v.size=" << v.size() << endl;
	cout << "v.capacity=" << v.capacity() << endl;

	/*
		resize之后,元素个数少了,但是内存依然很大,浪费空间。
		vector<int>(v) 匿名构造了一个v,大小时size 容量也是size,但是没有名字,运行完该行之后就被释放
		.swap(v);匿名对象和v交换,v变小了,匿名对象被系统回收
	
	*/


	cout << "\nresize之后:" << endl;
	v.resize(3);
	cout << "v.size=" << v.size() << endl;
	cout << "v.capacity=" << v.capacity() << endl;

	cout << "\n利用swap缩小容量" << endl;
	vector<int>(v).swap(v);
	cout << "v.size=" << v.size() << endl;
	cout << "v.capacity=" << v.capacity() << endl;

}

int main() {

	//test01();
	test02();

	system("pause");
	return 0;
}
预留空间

预留空间:reserve(int len);控制内存开辟次数,一次性找到需要的内存,无需后续再寻找。

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

//预留空间:reserve(int len);控制内存开辟次数


void test01()
{
	//无参构造
	vector<int> v1;

	//利用reserve预留空间,不要再找新空间,一次得到需要的大空间
	//如果不加这个,底下就要找30次内存空间,有了就一步到位
	v1.reserve(100000);

	
	int count = 0;
	int* p = NULL;
	//这么大的数,不是一次到位的,分多次开辟新空间,
	for (int i = 0; i < 100000; i++)
	{
		v1.push_back(i);

		//p始终指向v1的首地址,变化就说明更行了
		if (p!=&v1[0])
		{
			p = &v1[0];
			count++;
		}
	}
	cout << "开辟内存次数:" << count << endl;
	
}

int main() {

	test01();

	system("pause");
	return 0;
}

deque

deque两头都可以操作,这一点上比vector效率高。但是其读取效率比vector低。

构造 赋值 大小 读取
  1. 构造函数:默认构造 拷贝构造 n个elem 区间
  2. 赋值:= assign
  3. 大小 empty size resize
  4. 数据存取:同vector,有[]、at,back和front
#include <iostream>
#include <deque>
using namespace std;

//实质是由中控器,划分成一段段缓冲区,读书效率比vector低
//但是首插效率比vector高
//deque 没有容器的概念


/*
deque<T> deqT;             //默认构造形式
deque(beg, end);           //构造函数将[beg, end)区间中的元素拷贝给本身。
deque(n, elem);            //构造函数将n个elem拷贝给本身。
deque(const deque &deq);   //拷贝构造函数
*/

//打印,const限制只读,迭代器也要换成只读的迭代器
void printDeque(const deque<int> &d)
{
	for (deque<int>::const_iterator it=d.begin();it<d.end();it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

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

	printDeque(d1);

	//2.区间
	deque<int> d2(d1.begin(), d1.begin()+5);
	printDeque(d2);

	//3.n个elem
	deque<int> d3(5, 0);
	printDeque(d3);

	//4.拷贝构造
	deque<int>d4(d1);
	printDeque(d4);

}

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

void test02()
{
	deque<int>d1;
	for (int i = 0; i < 10; i++)
	{
		d1.push_back(i);
	}
	printDeque(d1);

	deque<int>d2;
	d2 = d1;
	printDeque(d2);

	deque<int>d3;
	d3.assign(d1.begin(), d1.end());
	printDeque(d3);

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

//大小,empty size resize 没有容量,因为可以无限扩充
void test03()
{
	deque<int>d1;
	for (int i = 0; i < 10; i++)
	{
		d1.push_back(i);
	}
	printDeque(d1);

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

	//resize

	cout << "\nafter resize" << endl;
	d1.resize(3);
	cout << "d1的大小:" << d1.size() << endl;
	printDeque(d1);

	d1.resize(20);
	printDeque(d1);
	d1.resize(30,5);
	printDeque(d1);

}

//数据存取,同vector,有[]、at,back和front

void test04()
{
	deque<int>d;
	d.push_back(1);
	d.push_back(2);
	d.push_back(3);

	cout << "d[0]=" << d[0] << endl;
	cout << "d.at(1)=" << d.at(1) << endl;
	cout << "d.back()=" << d.back() << endl;
	cout << "d.front()=" << d.front() << endl;
}



int main() {

	//1.构造函数
	//test01();

	//2.赋值
	//test02();

	//3.大小
	//test03();

	//4.存取
	test04();

	system("pause");
	return 0;
}
插入 删除
  1. 首插法 尾插法 首删法 尾删法
  2. 某一位置插入 一个元素 n个元素 某一区间的数
  3. 清空
#include <iostream>
#include <deque>
using namespace std;

//打印,const限制只读,迭代器也要换成只读的迭代器
void printDeque(const deque<int>& d)
{
	for (deque<int>::const_iterator it = d.begin(); it < d.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

void test01()
{
	deque<int> d1;
	for (int i = 0; i < 5; i++)
	{
		d1.push_back(i);//尾插法
		d1.push_front(i);//头插法
	}
	printDeque(d1);

	//删除头尾
	d1.pop_back();
	printDeque(d1);
	d1.pop_front();
	printDeque(d1);

	//插入,3个100,不要参数3,就是默认1个100
	d1.insert(d1.begin() + 4, 3,100);
	printDeque(d1);

	//某一位置插入某区间的数
	deque<int>d2;
	d2.push_back(10);
	d2.push_back(20);
	d2.push_back(30);

	d1.insert(d1.begin(), d2.begin(), d2.end());
	printDeque(d1);

	//删除某一位置
	d1.erase(d1.begin() + 4);
	printDeque(d1);

	//删除某一区间,必须提供迭代器,不能提供给1023这样的数字索引值
	d1.erase(d1.begin() + 4, d1.begin() + 6);
	printDeque(d1);

	//清空
	d1.clear();
	printDeque(d1);

}

int main() {

	//1.构造函数
	test01();

	system("pause");
	return 0;
}
排序

对于支持随即迭代器的容器,都能sort排序,需要包含头文件algorithm

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

//sort(beg,end) 排序该区间
//sort是标准算法,需要头文件algorithm
//对于支持随即迭代器的容器,都能sort排序

//输出函数,这里没有用迭代器,直接[]
void printDeque(const deque<int>& d)
{
	for (int i = 0; i < d.size(); i++)
	{
		cout << d[i] << " ";
	}
	cout << endl;
}

void test01()
{
	deque<int>d;
	for (int i = 0; i < 10; i++)
	{
		d.push_back(rand() % 21);
	}
	cout << "排序前:" << endl;
	printDeque(d);

	//排序
	//sort默认升序,从小到大
	//对于支持随即迭代器的容器,都能sort排序
	sort(d.begin(), d.end());
	cout << "排序后:" << endl;
	printDeque(d);
}

int main() {
	
	test01();

	system("pause");
	return 0;
}
案例 评委打分
#include <iostream>
#include <vector>
#include <string>
#include <deque>
#include <algorithm>
#include <ctime>
using namespace std;

class Person {

public:

	//构造函数
	Person(string name, deque<int>score, int ave)
	{
		this->m_Name = name;
		this->m_Score = score;
		this->m_Ave = ave;
	}

	//显示个人的裁判打分
	void showInfo()
	{
		cout << "选手姓名:" << this->m_Name << endl;
		cout << "评委打分:";
		for (deque<int>::iterator it=this->m_Score.begin();it!= this->m_Score.end();it++)
		{
			cout << *it << "  ";
		}
		cout << endl;
	}

	//算平均分
	double getAve()
	{
		//排序,去掉最高和最低
		sort(this->m_Score.begin(), this->m_Score.end());

		cout << "去除最低分:" << this->m_Score.front()<<"和最高分:"<<this->m_Score.back() << endl;

		this->m_Score.pop_back();
		this->m_Score.pop_front();

		double sum = 0;
		for (deque<int>::iterator it = this->m_Score.begin(); it != this->m_Score.end(); it++)
		{
			sum += *it;
		}

		this->m_Ave = sum / this->m_Score.size();

		cout << "最终成绩:" << this->m_Ave << endl << endl;

		return this->m_Ave;
	}

	//姓名
	string m_Name;
	//存储裁判打分的容器
	deque<int>m_Score;
	//平均分
	double m_Ave;

};

//初始化5名选手
vector<Person> createPerson()
{
	//创建容器
	vector<Person>v;

	string nameSeed = "ABCDE";
	for (int i = 0; i < 5; i++)
	{
		//姓名
		string name = "Person_";
		name += nameSeed[i];

		//默认构造,初始化裁判打分	
		deque<int>d;

		//平均分
		int ave = 0;

		//构造学生
		Person p(name, d, ave);

		//存入容器
		v.push_back(p);
	}

	return v;
}

//裁判打分,传入选手容器,本来就是要赋值,不要const修饰,传入引用即可
void setScore(vector<Person> &v)
{
	//10个分数,加入随机数,得放在循环外面,否则分数一样,原因未知
	srand((unsigned int)time(NULL));

	//开始便利
	for (vector<Person>::iterator it=v.begin();it<v.end();it++)
	{
		具体的选手,如果初始化是10个0,先清空容器,,换成默认构造,就不需要清空
		//it->m_Score.clear();
		double sum = 0;
		for (int i = 0; i < 10; i++)
		{
			//最低60 最高100
			int score = rand() % 41 + 60;
			//分数存入容器
			it->m_Score.push_back(score);
		}
	}
}

//展示每个人的信息
void showScore(vector<Person>& v)
{
	vector<double>v_S;
	for (vector<Person>::iterator it = v.begin(); it < v.end(); it++)
	{
		//调用成员函数
		it->showInfo();
		double ave=it->getAve();

		v_S.push_back(ave);
	}
	//排序平均分
	sort(v_S.begin(), v_S.end());
	//找到冠军
	double max = v_S.back();
	for (int i = 0; i < v.size(); i++)
	{
		if (v[i].m_Ave == max)
		{
			cout << "冠军是:" << v[i].m_Name << endl;
		}
	}
}


void test01()
{
	//创建选手
	vector<Person>v=createPerson();
	//设置分数
	setScore(v);
	//展示分数
	showScore(v);
}

int main() {

	test01();

	system("pause");
	return 0;
}

stack和queue

stack
  1. stack是一种栈容器,先进后出,正因如此,stack不允许便利可以.size(),这是入栈的时候统计的,也可以判断是否为空把栈理解成一个上端开口的盒子,先进的在下面,叫栈底
  2. 常用的就:top pop push size empty
#include <iostream>
#include <stack>
using namespace std;


/*stack是一种栈容器,先进后出,正因如此,stack不允许便利
可以.size(),这是入栈的时候统计的,也可以判断是否为空
把栈理解成一个上端开口的盒子,先进的在下面,叫栈底,*/

//常用的就:top pop push size empty

/*
构造函数:

* stack<T > stk; //stack采用模板类实现, stack对象的默认构造形式
* stack(const stack& stk);//拷贝构造函数

赋值操作:

* stack& operator=(const stack& stk); //重载等号操作符

数据存取:

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

大小操作:

* empty();            //判断堆栈是否为空
* size();             //返回栈的大小
*
*/
void test01()
{
	stack<int>s;

	s.push(10);
	s.push(20);
	s.push(30);
	s.push(40);


	cout << "栈的大小:" << s.size() << endl;

	cout << "栈内数据:";

	//没有遍历,通过循环访问栈顶获取
	while (!s.empty())
	{
		//返回栈顶元素
		cout << s.top() << " ";
		//弹出栈顶元素,下一个元素变成栈顶
		s.pop();
	}
	cout << endl;
	cout << "栈的大小:" << s.size() << endl;



}

int main() {

	test01();

	system("pause");
	return 0;
}
queue
#include <iostream>
#include <queue>
using namespace std;

/*Queue是一种先进先出的数据结构,它有两个出口,队头和队尾可以访问,不允许遍历
依然有默认构造,拷贝构造,等号赋值,
队头出数据,队尾进入数据*/

void test01()
{
	queue<int>q;

	q.push(10);
	q.push(20);
	q.push(30);
	q.push(40);


	cout << "大小:" << q.size() << endl;

	//没有遍历,通过循环访问栈顶获取
	while (!q.empty())
	{
		//返回队头
		cout <<"队头" << q.front() << " ";
		//返回队尾
		cout <<"队尾"<< q.back() << " ";
		cout << endl;
		//出队
		q.pop();
	}
	cout << "栈的大小:" << q.size() << endl;



}

int main() {

	test01();

	system("pause");
	return 0;
}

list链表

list,即链表,将数据进行链式储存,数据被存在一个个节点中,每一个节点分为数据域和指针域数据域中存放数据,指针域存放指向下一个节点地址的指针。如此,插入和删除数据更方便,但是不能随机访问,属于双向迭代器

- 如何判断叠加器是否随机 itrator it;it+1;如果不报错,就是随机的,可以+n
- 如何判断是否双向 ++ 和–都不报错就是双向

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

//list,即链表,将数据进行链式储存,数据被存在一个个节点中,每一个节点分为数据域和指针域
//数据域中存放数据,指针域存放指向下一个节点地址的指针
//如此,插入和删除数据更方便,但是不能随机访问,属于双向迭代器

//如何判断叠加器是否随机 itrator it;it+1;如果不报错,就是随机的,可以+n
//如何判断是否双向 ++ 和 --不报错就是双向

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

//构造函数
void test01()
{
	//1.默认构造
	list<int>ls1;
	for (int i = 0; i < 10; i++)
	{
		ls1.push_back(i);
	}
	printList(ls1);

	//2.拷贝构造
	list<int>ls2(ls1);
	printList(ls2);

	//3.区间
	list<int>ls3(ls1.begin(), ls1.end());
	printList(ls3);

	//4.n个elem
	list<int>ls4(10, 0);
	printList(ls4);
}

//赋值和交换
void test02()
{
	list<int>ls1;
	for (int i = 0; i < 10; i++)
	{
		ls1.push_back(i);
	}
	printList(ls1);

	//=
	list<int>ls2 = ls1;
	printList(ls1);

	//assign(n,elem)
	list<int>ls3;
	ls3.assign(10, 0);
	printList(ls3);

	//assign区间
	list<int>ls4;
	ls4.assign(ls1.begin(), ls1.end());
	printList(ls4);

	//交换
	ls3.swap(ls1);
	printList(ls1);
	printList(ls3);


}

//大小 size empty resize(n) resize(n,elem)
void test03()
{
	list<int>ls1;
	for (int i = 0; i < 10; i++)
	{
		ls1.push_back(i);
	}
	printList(ls1);

	if (ls1.empty())
	{
		cout << "list为空" << endl;
	}
	else
	{
		cout << "大小:" << ls1.size() << endl;
	}

	ls1.resize(3);
	printList(ls1);
	ls1.resize(5, 0);
	printList(ls1);

}

//数据存取 front和back
void test04()
{
	list<int>ls1;
	for (int i = 0; i < 10; i++)
	{
		ls1.push_back(i);
	}
	printList(ls1);

	cout << "首元素:" << ls1.front() << endl;
	cout << "尾元素:" << ls1.back() << endl;

}

/*插入和删除
首插、尾插、首删、尾删、
insert:位置元素 位置n个元素 位置区间 
erase:区间 位置
clear
remove(elem) 删除所有与elem匹配的元素
*/
void test05()
{
	list<int>ls1;
	for (int i = 0; i < 5; i++)
	{
		ls1.push_back(i);
		ls1.push_front(i);
	}
	printList(ls1);

	//删除首尾巴
	ls1.pop_back();
	ls1.pop_front();
	printList(ls1);

	//insert
	ls1.insert(ls1.begin(), 2, 3);
	printList(ls1);
	//不是连续的,不能+
	//ls1.insert(ls1.begin()+2, 2, 3);
	//但是可以通过迭代器
	list<int>::iterator it=ls1.begin();
	//it = it + 1;//防止+n,所以不让+,只能++
	it++;
	it++;
	//双向,不能跳,只能++ --
	it++;
	it--;
	ls1.insert(it, 2, 4);
	printList(ls1);

	//删除所有elem
	ls1.remove(3);
	printList(ls1);

	//清空
	ls1.clear();
	printList(ls1);
}

//降序的函数
bool myCompare(int va1,int va2)
{
	//降序 大就返回真 小就返回假
	return va1 > va2;
}

//反转和排序
void test06()
{
	list<int>ls1;
	for (int i = 0; i < 10; i++)
	{
		//生成0到99的随机数
		ls1.push_back(rand() % 10);
	}
	cout << "乱序:";
	printList(ls1);

	//升序
	//所有不支持随机访问迭代器的容器,不能用标准库的sort
	//这些容器会提供成员函数去排序
	ls1.sort();
	cout << "升序:";
	printList(ls1);

	//也可以自己写降序,要提供一个自定义函数
	ls1.sort(myCompare);
	cout << "降序:";
	printList(ls1);

	//反转
	ls1.reverse();
	cout << "反转:";
	printList(ls1);
}


int main() {

	//构造函数
	//test01();

	//赋值交换
	//test02();

	//大小
	//test03();

	//读取首位元素
	//test04();

	//插入删除
	//test05();

	//反转和排序
	test06();

	system("pause");
	return 0;
}
排序案例

自定义排序规则

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

//人有姓名 年龄 身高
//按年龄升序,如果年龄相同,则按身高降序

class Person {

public:

	Person(string name, int age, int height)
	{
		this->m_Name = name;
		this->m_Age = age;
		this->m_Height = height;
	}

	string m_Name;
	int m_Age;
	int m_Height;
};

//创造学生
list<Person> createPerson()
{
	//随机数种子
	srand((unsigned int)time(NULL));

	//创造5名学生
	list<Person>ls;

	string nameSeed = "ABCDE";
	for (int i = 0; i < 5; i++)
	{
		string name = "Person_";
		name += nameSeed[i];

		//年龄18到28
		int age = rand() % 11 + 18;

		//身高160到190
		int height = rand()%31 + 160;

		//构造
		Person p(name, age, height);

		//存入容器
		ls.push_back(p);
		
	}

	return ls;
}

//展示
void showPerson(const list<Person> &ls)
{
	for (list<Person>::const_iterator it=ls.begin();it!=ls.end();it++)
	{
		cout << "姓名:" << it->m_Name << "\t年龄:" << it->m_Age << "  身高:" << it->m_Height << endl;
	}
}

//自定义排序规则,由于Person是自定义数据类型,必须给出规则
//参数:比较两个Perosn类型
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()
{
	//创建学生
	list<Person>ls = createPerson();

	//展示
	cout << "排序前:" << endl;
	showPerson(ls);

	//排序,自定义数据类型,必须要有排序规则
	ls.sort(myCompare);

	//展示
	cout << "\n排序后:" << endl;
	showPerson(ls);

}

int main() {

	test01();

	system("pause");
	return 0;
}

set

set:所有元素在插入时自动被排序,底层由二叉树实现
multiset:允许有重复元素
set:不允许容器中有重复元素
set的insert会返回一个pair数据类型,有两个值,迭代器和bool,bool可以判断是否插入成功
而multiset就没有,可以重复
#include<iostream>
#include<set>
using namespace std;

/*
	set:所有元素在插入时自动被排序
	底层由二叉树实现
	multiset:允许有重复元素
	set:不允许容器中有重复元素
*/

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

//构造 赋值
void test01()
{

	/*set的insert会返回一个pair数据类型,有两个值,迭代器和bool,bool可以判断是否插入成功
	而multiset就没有,可以重复*/

	//默认构造
	set<int>s;
	//插入数据只有insert,没有push
	pair<set<int>::iterator,bool>ret= s.insert(1);
	if (ret.second)
	{
		cout << "插入成功" << endl;
	}
	else
	{
		cout << "插入失败" << endl;
	}

	s.insert(5);
	s.insert(2);
	s.insert(4);
	s.insert(3);
	ret=s.insert(1);//1重复了,打印的时候只有1个,可能没有传入
	//第二次插入失败,因为已经有一个1了,
	if (ret.second)
	{
		cout << "插入成功" << endl;
	}
	else
	{
		cout << "插入失败" << endl;

	}
	//所有元素在插入时自动排序,不允许重复值

	printSet(s);

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

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


//size empty swap
void test02()
{
	//默认构造
	set<int>s;
	//插入数据只有insert,没有push
	s.insert(1);
	s.insert(5);
	s.insert(2);
	s.insert(4);
	s.insert(3);
	//s.insert(5);//5重复了,打印的时候只有1个,没有传入,通过size可以知道
	s.insert(6);

	printSet(s);

	if (s.empty())
	{
		cout << "容器为空" << endl;
	}
	else
	{
		cout << "容器大小:" << s.size() << endl;
	}

	set<int>s2;
	s2.swap(s);
	if (s.empty())
	{
		cout << "容器为空" << endl;
	}
	else
	{
		cout << "容器大小:" << s.size() << endl;
	}
}

//insert clear erase:位置 区间 所有的elem
void test03()
{

	//默认构造
	set<int>s;
	//插入数据只有insert,没有push
	s.insert(1);
	s.insert(5);
	s.insert(2);
	s.insert(4);
	s.insert(3);
	s.insert(7);
	s.insert(6);
	printSet(s);

	//删除某一位置
	s.erase(s.begin());

	//删除所有的2,因为set不允许重复的,只有一个2
	//multiset可以有多个
	s.erase(2);
	printSet(s);

	//不允许随机访问,只能提供迭代器
	//s.erase(s.begin(), s.begin() + 2);
	set<int>::iterator it = s.begin();
	it++;
	it++;
	s.erase(s.begin(), it);
	printSet(s);

	//清空
	s.clear();
	printSet(s);

}

void test04()
{
	//默认构造
	set<int>s;
	//插入数据只有insert,没有push
	s.insert(1);
	s.insert(5);
	s.insert(2);
	s.insert(4);
	printSet(s);

	set<int>::iterator pos = s.find(20);
	//不存在会返回s.end()
	if (pos!=s.end())
	{
		//迭代器,实质是指针,解引用得到位置
		cout << "元素位置在" << *pos << endl;
	}
	else
	{
		cout << "元素不存在" << endl;
	}

	//统计
	int num = s.count(2);
	cout << "元素个数为" << num << endl;

}

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

void test05()
{
	multiset<int>ms;

	//可以有重复
	ms.insert(30);
	ms.insert(20);
	ms.insert(10);
	ms.insert(40);
	ms.insert(10);

	printMultiset(ms);

	//统计
	int num = ms.count(10);
	cout << "元素个数为" << num << endl;
}

int main() {

	//构造和赋值
	test01();

	//size empty swap
	//test02();

	//insert clear erase:位置 区间 所有的elem
	//test03();

	//查找和统计
	//test04();

	//set和multiset区别
	//test05();

	system("pause");
	return 0;
}
pair对组两种创建方式
#include <iostream>
#include <string>
using namespace std;

//pair对组的两种创建方式

void test01()
{
	//1.构造函数
	pair<string, int>p1("张三",22);
	cout << "p1姓名:" << p1.first << "\t年龄:" << p1.second << endl;

	//2.make_pair
	pair<string, int>p2=make_pair("李四", 23);
	cout << "p2姓名:" << p2.first << "\t年龄:" << p2.second << endl;
}

int main() {

	test01();

	system("pause");
	return 0;
}
set自定义排序规则-内置数据类型
#include <iostream>
#include <set>
using namespace std;

//利用仿函数修改set容器排序规则

class myCompare {

public:
	//重载等号运算符,仿函数,这里必须要有const
	//变成常函数,防止值被修改
	bool operator()(int val1,int val2)const
	{
		return val1 > val2;
	}

};

bool my_compare(int v1, int v2)
{
	return v1 > v2;
}

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

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

	//注意调用时的迭代器与定义时对应
	//这里只能用仿函数,不能用全局函数
	set<int,myCompare>s2;
	s2.insert(10);
	s2.insert(90);
	s2.insert(20);
	s2.insert(50);
	s2.insert(40);
	s2.insert(60);
	for (set<int,myCompare>::iterator it = s2.begin(); it != s2.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
	
}

int main() {

	test01();

	system("pause");
	return 0;
}
set自定义排序规则-自定义数据类型
#include <iostream>
#include <string>
#include <set>
using namespace std;

class Person {

public:

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

	string m_Name;
	int m_Age;
};

class myCompare {

public:
	//必须用俩const修饰形参,防止改变实参,并且也要const变成常函数
	bool operator()(const Person& p1, const Person& p2)const
	{
		return p1.m_Age > p2.m_Age;
	}
};

void test01()
{
	//自定义数据类型,必须给排序规则,否则不知道怎么插入
	set<Person,myCompare>s;

	Person p1("张三", 22);
	Person p2("李四", 21);
	Person p3("王五", 26);
	Person p4("赵六", 24);
	Person p5("钱七", 20);

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

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

int main(){

	test01();

	system("pause");
	return 0;
}

map

map中所有元素都是pair
pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)
所有元素都会根据元素的键值自动排序

map不允许容器中有重复key值元素,但实值可以
multimap允许容器中有重复key值元素
#include <iostream>
#include <string>
#include <map>
using namespace std;

/*
	map中所有元素都是pair
	pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)
	所有元素都会根据元素的键值自动排序

	map不允许容器中有重复key值元素,但实值可以
	multimap允许容器中有重复key值元素
*/

//打印,插入时按键值排序
void printMap(const map<int, int> &m)
{
	for (map<int, int>::const_iterator it=m.begin();it!=m.end();it++)
	{
		//解引用得到对组,对组有两个数,即键值和实值
		//依然有两种方式,解引用和指针
		cout << (*it).first<< " " << it->second << endl;
	}
}

//构造赋值
void test01()
{
	//1.默认构造

	//创建map容器,由于元素是pair,有两个值
	map<int, int>m;

	//这里是匿名对组,详见对组的创建方式
	m.insert(pair<int, int>(3, 30));
	m.insert(pair<int, int>(1, 10));
	m.insert(pair<int, int>(4, 40));
	m.insert(pair<int, int>(2, 20));
	//m.insert(pair<int, int>(2, 20));//键值重复不行

	printMap(m);

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

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

//size empty swap
void test02()
{
	map<int, int>m;
	m.insert(pair<int, int>(3, 30));
	m.insert(pair<int, int>(1, 10));
	m.insert(pair<int, int>(4, 40));

	if (!m.empty())
	{
		cout << "The size of m is" << m.size() << endl;
	}
	else
	{
		cout << "m is empty" << endl;
	}

	map<int, int>m2;
	m2.swap(m);
	if (!m.empty())
	{
		cout << "The size of m is" << m.size() << endl;
	}
	else
	{
		cout << "m is empty" << endl;
	}
}

//insert clear 
//erase:1.pos 2.区间 3.key(索引)

//打印
void printNameMap(const map<int, string>& m)
{
	for (map<int, string>::const_iterator it=m.begin();it!=m.end();it++)
	{
		cout << "序号:" <<it->first << "\t姓名:" << it->second << endl;
	}
}

void test03()
{
	//创建容器
	map<int, string>m;

	//insert
	//1.
	m.insert(pair<int, string>(1, "张三"));
	//2.推荐
	m.insert(make_pair(3, "李四"));
	//3.
	m.insert(map<int,string>::value_type(2, "王五"));
	//4.如果写成了[3],会覆盖前面的
	m[4]=("赵六");

	//不推荐[]赋值,如果没有存m[5],系统给你创了一个空value的对组
	//[]可以用来访问数据???
	//cout << "m[5]=" << m[5] << endl;
	//感觉都有问题,插数,key重复了,会覆盖前面,访问,不存在的key值系统会创建,所以最好别用[]
	cout << "初始:" << endl;
	printNameMap(m);

	//删除某一位置
	m.erase(m.begin());
	cout << "\n删除第一个位置:" << endl;
	printNameMap(m);

	//也可以按照区间

	//按key删除
	m.erase(3);
	cout << "\n删除序号3:" << endl;
	printNameMap(m);

	//清空
	m.clear();
	cout << "\n清空:" << endl;
	printNameMap(m);

}

//find count
void test04()
{
	map<int, int>m;
	m.insert(make_pair(1, 10));
	m.insert(make_pair(2, 20));
	m.insert(make_pair(3, 30));
	m.insert(make_pair(4, 40));
	m.insert(make_pair(5, 50));

	printMap(m);

	map<int,int>::iterator pos=m.find(2);
	if (pos != m.end())
	{
		cout << "找到了元素:key=" <<pos->first<<"  值:"<<pos->second << endl;
	}

	else 
	{
		cout << "未找到" << endl;
	}

	//multimap可以大于1
	int num = m.count(2);
	cout << "num:" << num << endl;

}

//排序

class myCompare {

public:

	bool operator()(int v1, int v2)const
	{
		return v1 > v2;
	}
};

void test05()
{
	map<int, int,myCompare>m;
	m.insert(make_pair(1, 10));
	m.insert(make_pair(4, 40));
	m.insert(make_pair(2, 20));
	m.insert(make_pair(5, 50));
	m.insert(make_pair(3, 30));

	//打印也必须提供对应的迭代器
	for (map<int, int, myCompare>::iterator it=m.begin();it!=m.end();it++)
	{
		cout << (*it).first << " " << it->second << endl;
	}
}

int main() {

	//构造赋值
	//test01();

	//size empty swap
	//test02();

	//insert clear 
	//erase:1.pos 2.区间 3.key(索引)
	//test03();

	//find count
	//test04();

	//排序
	test05();


	system("pause");
	return 0;
}
案例
  • ···maltimap如何value进行排序?
  • 本案例中,部门作为key进行排序,部门相同时,如何按员工工资进行排序?
公司今天招聘了10个员工(ABCDEFGHIJ),10名员工进入公司之后,需要指派员工在那个部门工作
员工信息有: 姓名  工资组成;部门分为:策划、美术、研发
随机给10名员工分配部门和工资
通过multimap进行信息的插入  key(部门编号) value(员工)
分部门显示员工信息
#include<iostream>
#include<map>
#include<string>
#include<vector>
#include<ctime>
using namespace std;

/*
	公司今天招聘了10个员工(ABCDEFGHIJ),10名员工进入公司之后,需要指派员工在那个部门工作
	员工信息有: 姓名  工资组成;部门分为:策划、美术、研发
	随机给10名员工分配部门和工资
	通过multimap进行信息的插入  key(部门编号) value(员工)
	分部门显示员工信息
*/

class Person
{
public:

	void showInfo()
	{
		cout << "姓名:" << this->m_Name << "\t工资:" << this->m_Salary << endl;
	}

	string m_Name;
	int m_Salary;
};

void createWorker(vector<Person>&v)
{
	//姓名种子
	string nameSeed = "ABCDEFGHIJ";
	//遍历
	for (int i = 0; i < 10; i++)
	{
		Person p;
		p.m_Name = "Person_";
		p.m_Name += nameSeed[i];

		p.m_Salary = 10000 + rand() % 10001;

		v.push_back(p);
	}
	//for (vector<Person>::iterator it=v.begin();it!=v.end();it++)
	//{
	//	it->showInfo();
	//}
}

//员工分组
void setGroup(vector<Person>& v, multimap<int, Person>& m)
{
	for (vector<Person>::iterator it = v.begin(); it != v.end(); it++)
	{
		//随机部门
		int dep = rand() % 3;
		m.insert(pair<int, Person>(dep, *it));
	}
}

void showPersonByGroup(multimap<int, Person>& m)
{
	cout << "策划部:" << endl;
	//pos是第一次找到0的位置,由于是已经排好位置,直接往后移动就行了
	//至于移动多少,先统计一下个数
	multimap<int, Person>::iterator pos = m.find(0);
	int count_0 = m.count(0);//统计总人数
	int index_0 = 0;
	for (;index_0<count_0;pos++,index_0++)
	{
		pos->second.showInfo();
	}

	cout << "美术部:" << endl;
	pos = m.find(1);
	int count_1 = m.count(1);//统计总人数
	int index_1 = 0;
	for (;index_1 < count_1; pos++, index_1++)
	{
		pos->second.showInfo();
	}

	cout << "研发部:" << endl;
	pos = m.find(2);
	int count_2 = m.count(2);//统计总人数
	int index_2 = 0;
	for (; index_2 < count_2; pos++, index_2++)
	{
		pos->second.showInfo();
	}

}

int main() {

	srand((unsigned int)time(NULL));
	
	//1.创建员工
	vector<Person>v;
	createWorker(v);

	//2.设置分组
	multimap<int, Person>m;
	setGroup(v, m);

	showPersonByGroup(m);

	system("pause");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值