文章目录
- 一、标准模板库STL
- 二、容器算法迭代器应用
- 1、遍历容器中整型数据
- 2、遍历容器中自定义数据类型
- 3、容器中嵌套容器
 
- 三、string容器
- 1、构造函数
- 2、赋值操作
- 3、字符串拼接
- 4、查找和替换
- 5、字符串比较
- 6、字符访问与存取
- 7、插入和删除
- 8、子串
 
- 四、vector容器
- 1、构造函数
- 2、赋值操作
- 3、容量和大小
- 4、插入和删除
- 5、数据存取
- 6、容器互换
- 7、预留空间
 
- 五、deque容器
- 1、构造函数
- 2、赋值操作
- 3、大小操作
- 4、插入和删除
- 5、数据存取
- 6、排序
 
- 六、评委打分案例
一、标准模板库STL
C++面向对象(封装、继承、多态)和泛型编程(模板)的思想,就是提升代码的复用性。大多数情况下,数据结构和算法都未能有一套标准,为了建立这样的标准,出现了STL。
 STL全称是Standard Template Library,即标准模板库。
 STL从广义上分为容器、算法、迭代器,容器和算法之间通过迭代器无缝衔接。STL几乎所有的代码都采用了模板类或者模板函数。
 STL的六大组件:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器。
 容器:各种数据结构,如string、vector、list、deque、set、map等,用来存放数据。
 算法:各种常用的算法,如sort、find、copy、for_each等。
 迭代器:扮演了容器与算法之间的胶合剂。
 仿函数:行为类似函数,可作为算法的某种策略。
 适配器:一种用来修饰容器或者仿函数或迭代器接口的东西。
 空间配置器:负责空间的配置与管理。
 容器就是将运用最广泛的一些数据结构实现出来。常用的数据结构:数组,链表,树,栈,队列,集合,映射表等,这些容器分为序列式容器和关联式容器。序列式容器强调值的排序,序列式容器中的每个元素均有固定的位置。关联式容器基于二叉树结构,各元素之间没有严格的物理上的顺序关系。
 算法用于解决逻辑或数学上的问题,其分为质变算法和非质变算法。质变算法是指运算过程中会更改区间内的元素的内容,例如拷贝、替换、删除等。非质变算法是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等。
 迭代器是容器和算法之间粘合剂,其提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。每个容器都有专属的迭代器,迭代器使用非常类似于指针。
| 迭代器的种类 | 功能 | 支持运算 | 
|---|---|---|
| 输入迭代器 | 对数据的只读访问 | ++、==、!= | 
| 输出迭代器 | 对数据的只写访问 | ++ | 
| 前向迭代器 | 读写操作,能向前推进迭代器 | ++、==、!= | 
| 双向迭代器 | 读写操作,能向前向后推进迭代器 | ++、- - | 
| 随机访问迭代器 | 读写操作,可以以跳跃的方式访问任意数据 | ++、- -、[n]、-n、<、<=、>、>= | 
双向迭代器和随机访问迭代器是较为常用的两种。
二、容器算法迭代器应用
1、遍历容器中整型数据
STL中最常用的容器是vector,可以将其理解为数组,下面通过代码向容器中插入数据,并遍历这个容器。
 涉及到的容器:vector,算法:for_each,迭代器:vector::iterator。
 容器、算法、迭代器的简单应用示例如下。
#include <iostream>
#include <vector>   //包含vector头文件
#include <algorithm>   //包含标准算法头文件
using namespace std;
void print(int value)  //for_each的回调函数
{
	cout<<value<<endl;
}
void fun()
{
	//1.创建vector容器
	vector<int> v;
	//2.向容器中插入数据
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	//3.通过迭代器遍历容器
	//方法一遍历——while循环
	//vector<int>::iterator it_begin = v.begin();  //起始迭代器,指向容器中的第一个元素
	//vector<int>::iterator it_end = v.end();  //结束迭代器,指向容器中最后一个元素的下一个位置
	//while(it_begin != it_end)
	//{
	//	cout<<*it_begin<<endl;  //*it_begin解引用得到的数据类型与容器定义的数据类型一致
	//	it_begin++;
	//}
	//方法二遍历——for循环
	//for(vector<int>::iterator i = v.begin();i!=v.end();i++)
	//{
	//	cout<<*i<<endl;
	//}
	//方法三遍历——需要包含算法头文件和回调函数
	for_each(v.begin(),v.end(),print);
}
int main()
{
	fun();
	system("pause");
	return 0;
}
上面代码中,push_back()函数用于向容器中插入数据,采用的是尾插法,begin()函数用于返回容器中第一个数据的位置,end()函数用于返回容器中最后一个数据的下一个位置。
 上面程序的运行结果如下图所示。
 
2、遍历容器中自定义数据类型
遍历容器中自定义数据类型的示例如下。
#include <iostream>
#include <vector>   //包含vector头文件
#include <algorithm>   //包含标准算法头文件
#include <string>
using namespace std;
class Person
{
public:
	Person(string name,int age)
	{
		this->name = name;
		this->age = age;
	}
	string name;
	int age;
};
void print(Person value)
{
	cout<<"姓名:"<<value.name<<" 年龄:"<<value.age<<endl;
}
void fun()
{
	Person p1("a",10);
	Person p2("b",20);
	Person p3("c",30);
	Person p4("d",40);
	vector<Person> v;
	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	//方法一遍历——while循环
	//vector<Person>::iterator it_begin = v.begin();  //起始迭代器,指向容器中的第一个元素
	//vector<Person>::iterator it_end = v.end();  //结束迭代器,指向容器中最后一个元素的下一个位置
	//while(it_begin != it_end)
	//{
	//	cout<<"姓名:"<<(*it_begin).name<<" 年龄:"<<(*it_begin).age<<endl;  //*it_begin解引用得到的数据类型是Person
	//  cout<<"姓名:"<<it_begin->name<<" 年龄:"<<it_begin->age<<endl;  //也可以通过指针的方式访问成员属性,与上面代码的作用一样
	//	it_begin++;
	//}
	//方法二遍历——for循环
	//for(vector<Person>::iterator i = v.begin();i!=v.end();i++)
	//{
	//	cout<<"姓名:"<<(*i).name<<" 年龄:"<<(*i).age<<endl;
	//}
	//方法三遍历——需要包含算法头文件和回调函数
	for_each(v.begin(),v.end(),print);
}
int main()
{
	fun();
	system("pause");
	return 0;
}
使用自定义数据类型时需要注意的是,在定义容器的时候也要使用自定义的数据类型,通过解引用得到的是自定义的数据类型,但是需要加上括号再访问成员变量,因为.的优先级高于解引用*的优先级。
 上面程序的运行结果如下图所示。
 
 容器中存放自定义数据类型地址的代码示例如下。
#include <iostream>
#include <vector>   //包含vector头文件
#include <algorithm>   //包含标准算法头文件
#include <string>
using namespace std;
class Person
{
public:
	Person(string name,int age)
	{
		this->name = name;
		this->age = age;
	}
	string name;
	int age;
};
void print(Person* value)
{
	cout<<"姓名:"<<value->name<<" 年龄:"<<value->age<<endl;
}
void fun()
{
	Person p1("a",10);
	Person p2("b",20);
	Person p3("c",30);
	Person p4("d",40);
	vector<Person *> v;
	v.push_back(&p1);
	v.push_back(&p2);
	v.push_back(&p3);
	v.push_back(&p4);
	//方法一遍历——while循环
	//vector<Person *>::iterator it_begin = v.begin();  //起始迭代器,指向容器中的第一个元素
	//vector<Person *>::iterator it_end = v.end();  //结束迭代器,指向容器中最后一个元素的下一个位置
	//while(it_begin != it_end)
	//{
	//	cout<<"地址:"<<&(*it_begin)<<endl;
	//	cout<<"姓名:"<<(*it_begin)->name<<" 年龄:"<<(*it_begin)->age<<endl;
	//	it_begin++;
	//}
	//方法二遍历——for循环
	for(vector<Person *>::iterator i = v.begin();i!=v.end();i++)
	{
		cout<<"地址:"<<&(*i)<<endl;
		cout<<"姓名:"<<(*i)->name<<" 年龄:"<<(*i)->age<<endl;
	}
	//方法三遍历——需要包含算法头文件和回调函数
	//for_each(v.begin(),v.end(),print);
}
int main()
{
	fun();
	system("pause");
	return 0;
}
上面程序的运行结果如下图所示。
 
3、容器中嵌套容器
前面已经说过,容器相当于数组,在一个容器中再嵌套一个容器就相当于一个二维数组。
 容器中嵌套容器的代码示例如下。
#include <iostream>
#include <vector>   //包含vector头文件
#include <algorithm>   //包含标准算法头文件
using namespace std;
void fun()
{
	vector<vector<int>> v;  //容器中嵌套容器
	vector<int> v1;  //创建小容器
	vector<int> v2;
	vector<int> v3;
	for(int i=0;i<3;i++)   //给每个小容器插值
	{
		v1.push_back(i+1);
		v2.push_back(i+4);
		v3.push_back(i+7);
	}
	v.push_back(v1);  //给大容器中插入小容器
	v.push_back(v2);
	v.push_back(v3);
	
	for(vector<vector<int>>::iterator i = v.begin();i!=v.end();i++)  //遍历大容器
	{
		//*i此时是<vector<int>类型,还是一个容器,<>中是什么类型,解引用得到的就是什么类型
		for(vector<int>::iterator j = (*i).begin();j!=(*i).end();j++) //遍历小容器
		{
			cout<<*j<<" ";
		}
		cout<<endl;
	}
}
int main()
{
	fun();
	system("pause");
	return 0;
}
上面程序的运行结果如下图所示。
 
三、string容器
string是C++风格的字符串,string本质上是一个类,类的内部封装了一个char类型的指针来管理这个字符串,是一个char类型的容器。
 string类的内部封装了很多成员方法,比如查找find、拷贝copy、删除delete、替换replace、插入insert等,string管理char*所分配的内存,由类内部负责,因此不用担心复制越界和取值越界的问题。
1、构造函数
string的构造函数类型包括:
string()——创建一个空的字符串,默认的空实现;
string(const char *str)——使用字符串str初始化;
string(const string &str)——使用一个string对象初始化另一个string对象;
string(int n, char c)——使用n个字符c初始化字符串。
代码中分别使用上面的四种方式创建字符串的代码示例如下。
#include <iostream>
#include <string>
using namespace std;
void fun()
{
	string str1;
	cout<<"str1="<<str1<<endl;
	const char* str = "hello";
	string str2(str);
	cout<<"str2="<<str2<<endl;
	string str3(str2);
	cout<<"str3="<<str3<<endl;
	string str4(5,'a');
	cout<<"str4="<<str4<<endl;
}
int main()
{
	fun();
	system("pause");
	return 0;
}
上面程序的运行结果如下图所示。
 
2、赋值操作
string赋值操作包括以下几种。
string& operator=(const char* s);
string& operator=(const string &s);
string& operator=(char c);    //把单个字符赋值给字符串
string& assign(const char* s);   //通过调用assign()函数赋值
string& assign(const char* s, int n);   //把字符串s的前n个字符赋值给当前字符串,注意第一个参数的类型不能是string
string& assign(const string &s);
string& assign(int n, char c);
string的几种赋值操作代码示例如下。
#include <iostream>
#include <string>
using namespace std;
void fun()
{
	string str1;
	str1 = "helloworld1";
	cout<<"str1="<<str1<<endl;
	string str2;
	str2 = str1;
	cout<<"str2="<<str2<<endl;
	string str3;
	str3 = 'a';
	cout<<"str3="<<str3<<endl;
	string str4;
	str4.assign("helloworld4");
	cout<<"str4="<<str4<<endl;
	string str5;
	str5.assign("helloworld4",5);
	cout<<"str5="<<str5<<endl;
	string str6;
	str6.assign(str5);
	cout<<"str6="<<str6<<endl;
	string str7;
	str7.assign(5,'a');
	cout<<"str7="<<str7<<endl;
}
int main()
{
	fun();
	system("pause");
	return 0;
}
上面程序的运行结果如下图所示。
 
3、字符串拼接
string字符串拼接操作主要有以下几种。
string& operator+=(const char* s);
string& operator+=(char c);
string& operator+=(const string &s);
string& append(const char* s);
string& append(const char* s, int n);  
string& append(const string &s);
string& append(const string &s, int pos, int n);  //字符串s中从pos开始的n个字符连接到字符串结尾
string字符串拼接的几种操作代码示例如下。
#include <iostream>
#include <string>
using namespace std;
void fun()
{
	string str = "spo";
	str += "rts";
	cout<<"str="<<str<<endl;
	str += ':';
	cout<<"str="<<str<<endl;
	string str1 = "basketball";
	str += str1;
	cout<<"str="<<str<<endl;
	str.append(" football");
	cout<<"str="<<str<<endl;
	str.append(" baseballabcdef",9);
	cout<<"str="<<str<<endl;
	string str2 = " volleyball";
	str.append(str2);
	cout<<"str="<<str<<endl;
	str.append(" ");
	str.append(str2,7,4);
	cout<<"str="<<str<<endl;
}
int main()
{
	fun();
	system("pause");
	return 0;
}
上面程序的运行结果如下图所示。
 
4、查找和替换
string查找和替换的函数原型如下。
int find(const string& s, int pos = 0) const;   //查找s第一次出现位置,从pos开始查找,默认是下标为0的位置
int find(const char* s, int pos = 0) const;
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& s, int pos = npos)const;   //查找str最后一次位置,从pos开始查找,默认是字符串最后一个字符的下标
int rfind(const char* s, int pos =npos) const;
int rfind(const char* s, int pos, int n) const;  //从pos查找s的前n个字符最后一次位置
int rfind(const char c, int pos = 0) const;  //查找字符c最后一次出现位置
//rfind从后往前找,find从前往后找
string& replace(int pos, int n, const string& s);   //替换从pos开始的n个字符为字符串s,两者的数量不一定相等
string& replace(int pos, int n,const char* s);
查找和替换的例子如下。
#include <iostream>
#include <string>
using namespace std;
void fun1()
{
	int pos;
	string str = "hellowordhelloword";
	pos = str.find("llo");
	cout<<"pos="<<pos<<endl;
	pos = str.rfind("llo");
	cout<<"pos="<<pos<<endl;
	pos = str.find("loo");   //字符串不存在就返回-1
	cout<<"pos="<<pos<<endl;
}
void fun2()
{
	string str = "hood";
	str.replace(1,2,"ellowor");  //这里的替换在数量上不是一一对应关系
	cout<<"str="<<str<<endl; 
}
int main()
{
	fun1();
	fun2();
	system("pause");
	return 0;
}
上面程序的运行结果如下图所示。
 
5、字符串比较
字符串比较是按照字符的ASCII进行对比的。字符串相等返回0,大于返回1,小于返回-1。
 字符串比较的函数原型如下。
int compare(const string& s) const;  
int compare(const char* s) const;  
函数的实现也很简单,如下。
void fun()
{
	string str1 = "hello";
	string str2 = "hello";
	if(str1.compare(str2) == 0)
	{
		cout<<"str1 = str2"<<endl;
	}
	else if(str1.compare(str2) > 0)
	{
		cout<<"str1 > str2"<<endl;
	}
	else
	{
		cout<<"str1 < str2"<<endl;
	}
}
6、字符访问与存取
对string字符串中的单个字符访问有两种方式,一种是通过[]下标的方式,另一种是通过函数at()进行访问。
 关于字符访问的两种方式,其代码如下。
#include <iostream>
#include <string>
using namespace std;
void fun()
{
	string str = "hello";
	//方式一,数组下标
	for(int i=0;i<str.size();i++)
	{
		cout<<str[i];
	}
	cout<<endl;
	//方式二,at()
	for(int i=0;i<str.size();i++)
	{
		cout<<str.at(i);
	}
	cout<<endl;
	//修改字符串中的某些位
	str[2] = 'a';
	cout<<"str="<<str<<endl;
	str.at(3) = 'b';
	cout<<"str="<<str<<endl;
}
int main()
{
	fun();
	system("pause");
	return 0;
}
7、插入和删除
插入和删除函数的原型如下。
string& insert(int pos,const char* s);
string& insert(int pos,const string& s);
string& insert(int pos,int n,char c);   //在指定位置插入n个字符c
string& erase(int pos,int n=npos);   //删除从pos开始的n个字符,不给定第二个参数就删除到字符串结束
插入和删除的具体实现代码如下。
void fun()
{
	string str = "hello";
	str.insert(2,"abcd");
	cout<<"str="<<str<<endl;
	str.erase(2,4);
	cout<<"str="<<str<<endl;
}
8、子串
string子串获取的函数原型如下。
string substr(int pos=0,int n=npos)const;  //返回由pos开始的n个字符组成的字符串
string子串截取的简单应用例子如下。
void fun()
{
	string str = "zhangsan@qq.com";
	int pos = str.find('@');  //找到截取停止的位置
	string user = str.substr(0,pos);
	cout<<"user="<<user<<endl;
}
四、vector容器
vector数据结构和数组非常相似,也称为单端数组,其与数组的不同之处在于数组是静态空间,而vector可以动态扩展。
 动态扩展并不是在原有空间之后续接新空间,而是找一块更大的内存空间,将原数据拷贝到新的空间并释放原有空间。
 vector容器的简单示意图如下。
 
 vector容器是单端开口进行相关操作的,该容器的迭代器是支持随机访问的迭代器。
1、构造函数
vector容器有四种构造函数,在下面的代码中都有体现。
#include <iostream>
#include <vector>
#include <string>
using namespace std;
void printVector(vector<int> &v)
{
	for(vector<int>::iterator i=v.begin();i!=v.end();i++)
	{
		cout<<*i<<" ";
	}
	cout<<endl;
}
void fun()
{
	vector<int> v1;  //1.默认构造
	for(int i=0;i<5;i++)
	{
		v1.push_back(i+1);
	}
	cout<<"默认构造"<<endl;
	printVector(v1);
	vector<int> v2(v1.begin(),v1.begin()+3);  //2.通过区间方式进行构造
	cout<<"通过区间方式进行构造"<<endl;
	printVector(v2);
	vector<int> v3(5,1);  //3.通过n个相同的数据构造
	cout<<"通过n个相同的数据构造"<<endl;
	printVector(v3);
	vector<int> v4(v1);  //4.拷贝构造
	cout<<"拷贝构造"<<endl;
	printVector(v4);
}
int main()
{
	fun();
	system("pause");
	return 0;
}
上面程序的运行结果如下图所示。
 
2、赋值操作
容器赋值操作有两种方式,=赋值和assign指定区间赋值,具体的代码示例如下。
#include <iostream>
#include <vector>
#include <string>
using namespace std;
void printVector(vector<int> &v)
{
	for(vector<int>::iterator i=v.begin();i!=v.end();i++)
	{
		cout<<*i<<" ";
	}
	cout<<endl;
}
void fun()
{
	vector<int> v1;
	for(int i=0;i<5;i++)
	{
		v1.push_back(i+1);
	}
	printVector(v1);
	vector<int> v2; 
	v2 = v1;    // = 赋值
	printVector(v2);
	vector<int> v3;
	v3.assign(v1.begin(),v1.end());  //左开右闭区间
	printVector(v3);
	vector<int> v4;
	v4.assign(3,6);
	printVector(v4);
}
int main()
{
	fun();
	system("pause");
	return 0;
}
上面程序的运行结果如下图所示。
 
3、容量和大小
对vector容器容量和大小操作的相关函数如下。
empty();  //判断容器是否为空
capacity();  //获取容器容量
size();  //获取容器中元素的个数
resize(int num);  //重新指定容器长度num,容器变长以默认值填充,容器变短超出部分被删除
resize(int num,elem);  //重新指定容器长度num,容器变长以elem填充
关于容器容量和大小操作的应用示例如下。
#include <iostream>
#include <vector>
#include <string>
using namespace std;
void printVector(vector<int> &v)
{
	for(vector<int>::iterator i=v.begin();i!=v.end();i++)
	{
		cout<<*i<<" ";
	}
	cout<<endl;
}
void fun()
{
	vector<int> v;
	for(int i=0;i<10;i++)
	{
		v.push_back(i+1);
	}
	printVector(v);
	if(!v.empty())
	{
		cout<<"容器不为空!"<<endl;
		cout<<"容器的容量为:"<<v.capacity()<<endl;
		cout<<"容器的大小为:"<<v.size()<<endl;
	}
	else
	{
		cout<<"容器为空!"<<endl;
	}
	v.resize(5);   //容器变短,截取前5个元素
	printVector(v);
	v.resize(10,1);   //容器变长,自定义数据填充后面的元素
	printVector(v);
	v.resize(15);   //容器变长,以0填充后面的元素
	printVector(v);
}
int main()
{
	fun();
	system("pause");
	return 0;
}
上面程序的运行结果如下图所示。
 
4、插入和删除
对vector容器插入和删除操作的相关函数如下。
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();  //删除容器中所有元素
不同于string容器的插入和删除,其可以提供索引值在指定位置进行插入和删除,vector容器在插入和删除的时候需要提供迭代器,使用索引值是不行的。
 插入和删除操作的应用示例如下。
#include <iostream>
#include <vector>
#include <string>
using namespace std;
void printVector(vector<int> &v)
{
	for(vector<int>::iterator i=v.begin();i!=v.end();i++)
	{
		cout<<*i<<" ";
	}
	cout<<endl;
}
void fun()
{
	vector<int> v;
	for(int i=0;i<3;i++)
	{
		v.push_back(i+1);
	}
	printVector(v);
	v.pop_back();  //删除最后一个元素
	printVector(v);
	v.insert(v.begin()+1,5);  //在迭代器指向的位置处插入元素
	printVector(v);
	v.insert(v.begin()+2,3,6);  //在迭代器指向的位置处插入n个元素
	printVector(v);
	v.erase(v.begin());  //删除迭代器指向的元素
	printVector(v);
	v.erase(v.begin(),v.begin()+2);  //删除迭代器指向区间的元素
	printVector(v);
	v.clear();  //清空容器中所有元素
	//v.erase(v.begin(),v.end());   //与v.clear()的作用相同
	printVector(v);
}
int main()
{
	fun();
	system("pause");
	return 0;
}
上面程序的运行结果如下图所示。
 
5、数据存取
和vector容器中数据存取操作有关的函数如下。
at(int index);  //返回索引index指向的元素
operator[int index];  //返回索引index指向的元素
front();  //返回容器中的第一个元素
back();  //返回容器中的最后一个元素
数据存取的简单示例如下。
void fun()
{
	vector<int> v;
	for(int i=0;i<5;i++)
	{
		v.push_back(i+1);
	}
	cout<<v.at(3)<<endl;  //下标为3的元素
	cout<<v[3]<<endl;
	cout<<v.front()<<endl;  //第一个元素
	cout<<v.back()<<endl;   //最后一个元素
}
6、容器互换
容器互换指的是两个容器内元素进行互换,使用的函数是swap(v)。
 容器互换的应用示例如下。
#include <iostream>
#include <vector>
#include <string>
using namespace std;
void printVector(vector<int> &v)
{
	for(vector<int>::iterator i=v.begin();i!=v.end();i++)
	{
		cout<<*i<<" ";
	}
	cout<<endl;
}
void fun1()
{
	vector<int> v1;
	vector<int> v2;
	for(int i=0;i<5;i++)
	{
		v1.push_back(i+1);
		v2.push_back(i+6);
	}
	cout<<"v1容器内元素:";
	printVector(v1);
	cout<<"v2容器内元素:";
	printVector(v2);
	
	v1.swap(v2);  //互换两容器
	cout<<"v1容器和v2容器互换后:"<<endl;
	cout<<"v1容器内元素:";
	printVector(v1);
	cout<<"v2容器内元素:";
	printVector(v2);
}
void fun2()   //实际的应用
{
	vector<int> v;
	for(int i=0;i<1000;i++)
	{
		v.push_back(i+1);
	}
	cout<<"1.v的容量:"<<v.capacity()<<endl;
	cout<<"1.v的大小:"<<v.size()<<endl;
	v.resize(3);  //重新指定容器大小
	cout<<"2.v的容量:"<<v.capacity()<<endl;
	cout<<"2.v的大小:"<<v.size()<<endl;
	vector<int>(v).swap(v);  //创建匿名对象和当前的容器进行互换,匿名对象在本行执行完后就会被回收
	//相当于将指向容器的指针和指向匿名对象的指针进行了交换,大的内存在本行执行完后被系统回收
	cout<<"3.v的容量:"<<v.capacity()<<endl;
	cout<<"3.v的大小:"<<v.size()<<endl;
}
int main()
{
	fun1();
	fun2();
	system("pause");
	return 0;
}
上面程序的运行结果如下图所示。
 
 可以看到,通过创建匿名对象和本容器进行交换,就可以收缩容器的容量,减小其占用的内存。
7、预留空间
vector的预留空间可以减少vector在动态扩展容量时的扩展次数。
reserve(int len);  //容器预留len个元素长度,预留的位置不初始化,元素不可访问
关于容器预留空间的示例如下。
#include <iostream>
#include <vector>
#include <string>
using namespace std;
void fun1()  //不预留空间
{
	vector<int> v;
	int count = 0;
	int *p = NULL;
	for(int i=0;i<10000;i++)
	{
		v.push_back(i+1);
		if(p!=&v[0])
		{
			p = &v[0];
			count++;
		}
	}
	cout<<"开辟内存的次数:"<<count<<endl;
}
void fun2()  //预留空间
{
	vector<int> v;
	int count = 0;
	int *p = NULL;
	v.reserve(10000);   //一开始就预留空间
	for(int i=0;i<10000;i++)
	{
		v.push_back(i+1);
		if(p!=&v[0])
		{
			p = &v[0];
			count++;
		}
	}
	cout<<"预留空间后开辟内存的次数:"<<count<<endl;
}
int main()
{
	fun1();
	fun2();
	system("pause");
	return 0;
}
上面程序的运行结果如下图所示。
 
五、deque容器
vector容器是单端数组,deque容器是双端数组,对于头部和尾部都可以进行插入和删除操作。
 vector容器和deque容器的区别在于:vector对于头部的插入和删除效率太低,数据量越大,其效率就越低;deque容器在头部的插入删除操作速度比vector快;vector访问元素的速度比deque快。
 deque容器的简单示意图如下图所示。
 
 deque容器内部的工作原理:内部有一个中控器,其维护着每段缓冲区的内容,缓冲区中存放着真实的数据,中控器维护的是每个缓冲区的地址,这就使得使用deque时像一片连续的内存空间。
 
 deque容器的迭代器也是支持随机访问的,即跳跃性的访问容器里面的元素。
1、构造函数
deque容器的构造函数和vector容器的构造函数类似,其应用示例如下。
#include <iostream>
#include <deque>
#include <string>
using namespace std;
void printDeque(const deque<int> &d) //限制为只读,不能修改
{
	for(deque<int>::const_iterator i=d.begin();i!=d.end();i++)
	{
		cout<<*i<<" ";
	}
	cout<<endl;
}
void fun()
{
	deque<int> d1;  //默认构造
	for(int i=0;i<5;i++)
	{
		d1.push_back(i+1);
	}
	printDeque(d1);
	deque<int> d2(d1);  //拷贝构造
	printDeque(d2);
	deque<int> d3(d1.begin(),d1.begin()+3);  //区间构造
	printDeque(d3);
	deque<int> d4(5,1);  //自定义数值构造
	printDeque(d4);
}
int main()
{
	fun();
	system("pause");
	return 0;
}
上面程序的运行结果如下图所示。
 
2、赋值操作
与vector容器一样,deque容器进行赋值时有=赋值和assign赋值两种方式。
 赋值操作的简单示例如下。
void fun()
{
	deque<int> d1;
	for(int i=0;i<5;i++)
	{
		d1.push_back(i+1);
	}
	printDeque(d1);
	deque<int> d2;
	d2 = d1;  // =赋值
	printDeque(d2);
	deque<int> d3;
	d3.assign(d1.begin(),d1.end());  //assign指定区间赋值
	printDeque(d3);
	deque<int> d4;
	d4.assign(5,6);  //assign指定数值进行赋值
	printDeque(d4);
}
3、大小操作
不同于vector容器,对于deque容器的操作中没有获取其容量的函数,即没有capacity()。
empty();  //判断容器是否为空
size();  //获取容器中元素的个数
resize(int num);  //重新指定容器长度num,容器变长以默认值填充,容器变短超出部分被删除
resize(int num,elem);  //重新指定容器长度num,容器变长以elem填充
关于deque容器大小操作的示例如下。
void fun()
{
	deque<int> d;
	for(int i=0;i<5;i++)
	{
		d.push_back(i+1);
	}
	printDeque(d);
	if(!d.empty())
	{
		cout<<"容器不为空!"<<endl;
		cout<<"容器的大小为:"<<d.size()<<endl;
	}
	d.resize(7);
	printDeque(d);
	d.resize(10,1);
	printDeque(d);
	d.resize(3);
	printDeque(d);
}
4、插入和删除
相比于vector容器的插入和删除,deque容器的插入和删除稍微复杂一点,因为其不仅涉及尾部的操作,还涉及头部的操作。
push_front(elem);  //在容器头部插入一个数据
push_back(elem);  //在容器尾部插入一个数据
pop_front();  //从容器头部删除一个数据
pop_back();  //从容器尾部删除一个数据
insert(pos,elem);  //在指定位置插入指定元素
insert(pos,n,elem);  //在指定位置插入n个指定元素
insert(pos,start_pos,end_pos); //从指定位置开始插入区间数据
clear(); //清空数据
erase(pos); //删除指定位置的数据
erase(start_pos,end_pos);  //删除指定区间的数据
关于deque容器插入和删除的简单操作示例如下。
#include <iostream>
#include <deque>
#include <string>
using namespace std;
void printDeque(const deque<int> &d) //限制为只读,不能修改
{
	for(deque<int>::const_iterator i=d.begin();i!=d.end();i++)
	{
		cout<<*i<<" ";
	}
	cout<<endl;
}
void fun()
{
	deque<int> d;
	d.push_back(1);
	d.push_back(2);
	d.push_front(3);
	d.push_front(4);
	printDeque(d);
	d.pop_back();
	d.pop_front();
	printDeque(d);
	d.insert(d.begin(),6);   //使用插入函数的时候需要提供迭代器
	d.insert(d.end(),3,6);
	printDeque(d);
	deque<int> d1;
	d1.push_back(10);
	d1.push_back(20);
	d.insert(d.begin(),d1.begin(),d1.end());  //在容器的指定位置处从插入另一个容器的某一段区间
	printDeque(d);
	d.erase(d.begin());
	printDeque(d);
	d.clear();
	printDeque(d);
}
int main()
{
	fun();
	system("pause");
	return 0;
}
上面程序的运行结果如下图所示。
 
5、数据存取
deque容器的数据存取有以下几个函数可使用。
at(int index);
operator[];
front();  //返回容器中第一个数据
back();   //返回容器中最后一个数据
关于数据存取的简单示例如下。
void fun()
{
	deque<int> d;
	d.push_back(1);
	d.push_back(2);
	d.push_back(3);
	for(int i=0;i<d.size();i++)
	{
		//cout<<d[i]<<" ";
		cout<<d.at(i)<<" ";
	}
	cout<<endl;
	cout<<"第一个元素是:"<<d.front()<<endl;
	cout<<"最后一个元素是:"<<d.back()<<endl;
}
6、排序
通过sort()就可以对deque容器进行排序,函数原型如下。
sort(iterator begin,iterator end);   //对区间内的数据进行排序
在使用sort()函数之前,需要先包含头文件 algorithm。关于deque容器排序的简单应用示例如下。
#include <iostream>
#include <deque>
#include <algorithm>
using namespace std;
void printDeque(const deque<int> &d) //限制为只读,不能修改
{
	for(deque<int>::const_iterator i=d.begin();i!=d.end();i++)
	{
		cout<<*i<<" ";
	}
	cout<<endl;
}
void fun()
{
	deque<int> d;
	d.push_back(2);
	d.push_back(1);
	d.push_back(6);
	d.push_back(4);
	d.push_back(5);
	d.push_back(3);
	cout<<"排序前:"<<endl;
	printDeque(d);
	sort(d.begin(),d.end());  //默认是升序排列
	cout<<"排序后:"<<endl;
	printDeque(d);
}
int main()
{
	fun();
	system("pause");
	return 0;
}
上面程序的运行结果如下图所示。
 
 需要说明的是,支持随机访问的迭代器的容器,都可以利用sort算法直接对容器中的元素进行排序,比如vector容器也是可以的。
六、评委打分案例
有五位选手ABCDE,10个评委分别对每一名选手打分,去除一个最高分和一个最低分,将平均分输出。
 实现步骤:
 1.创建选手类,添加属性,实例化五个对象,依次将其放到vector容器中;
 2.遍历vector容器,把每一个选手的得分情况存到deque容器中;
 3.对于每个选手的得分,使用sort算法对deque容器中的元素进行排序,去掉首尾元素;
 4.遍历deque容器,将剔除最高分和最低分后的分数相加并求平均值。
 该案例的代码如下。
#include <iostream>
#include <string>
#include <vector>
#include <deque>
#include <algorithm>
#include <ctime>
using namespace std;
class Person
{
public:
	Person(string name,double score)
	{
		this->name = name;
		this->score = score;
	}
	string name;
	double score;
};
void createPerson(vector<Person> &v)
{
	for(int i=0;i<5;i++)
	{
		double score = 0;
		string nameSeed = "ABCDE";
		string name = "选手";
		name += nameSeed[i];
		Person p(name,score);
		v.push_back(p);
	}
}
void setScore(vector<Person> &v)
{
	for(vector<Person>::iterator i=v.begin();i!=v.end();i++)
	{
		deque<int> d;
		for(int j=0;j<10;j++)
		{
			int num = rand()%41 + 60;  //60-100之间的随机数
			d.push_back(num);
		}
		
		cout<<i->name<<"的得分情况:"<<endl;
		for(deque<int>::iterator j=d.begin();j!=d.end();j++)
		{
			cout<<*j<<" ";
		}
		cout<<endl;
		sort(d.begin(),d.end());  //对deque容器中的元素进行升序排序
		d.pop_front();  //去除最低分
		d.pop_back();  //去除最高分
		int sum = 0;
		for(deque<int>::iterator j=d.begin();j!=d.end();j++)
		{
			sum += *j;  //求分数总和
		}
		double aver_score = sum / d.size();   //获取平均分
		i->score = aver_score;   //将求得的平均分存放在成员属性中
	}
}
void printScore(vector<Person> &v)
{
	for(vector<Person>::iterator i=v.begin();i!=v.end();i++)
	{
		cout<<(*i).name<<"的平均分是:"<<(*i).score<<endl;
	}
}
int main()
{
	srand((unsigned int)time(NULL));  //加入随机数种子,保证生成数的随机性
	//将5位选手存入vector容器中
	vector<Person> v;
	createPerson(v);
	//评委打分
	setScore(v);
	//打印平均分
	printScore(v);
	system("pause");
	return 0;
}
上面程序的运行结果如下图所示。
 
 本案例是比较综合的一个例子,包括了string容器、vector容器和deque容器。
本文参考视频:
 黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难


















