C++笔记8:C++提高编程2:STL---标准模板库

0831

1、模板

函数模板和类模板

2、初识STL

2、初识STL
2.1 诞生
为提升代码的复用性,C++提供了面向对象编程思想泛型编程思想。为了建立数据结构算法的一套标准,诞生了STL
面向对象的编程思想:封装、继承、多态
泛型编程思想:模板Template

2.2 STL基本概念
STL(Standard Template Library,标准模板库)。
另外,STL 几乎所有的代码都采用了模板类或者模板函数,见函数模板的总结

2.3 STL六大组件
六大组件为:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器。主要学习前四大组件

  1. 容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据。
  2. 算法:各种常用的算法,如sort、find、copy、for_each等
  3. 迭代器:扮演了容器与算法之间的胶合剂。
  4. 仿函数:行为类似函数,可作为算法的某种策略。见6.函数调用运算符()重载—仿函数 —3.7.7、3.7.8、3.8.8也有涉及到仿函数
  5. 适配器:一种用来修饰容器或者仿函数或迭代器接口的东西。
  6. 空间配置器:负责空间的配置与管理。

2.4 容器、算法、迭代器
STL 从广义上分为: 容器(container) 、算法(algorithm) 、迭代器(iterator)
三者之间的关系(重点记!!!):
容器算法之间通过迭代器进行无缝连接,或者说算法要通过迭代器才能访问容器中的数据。
②每个容器都有自己专属的迭代器,迭代器用来遍历容器中的元素
③ 容器-相当于-数据结构
算法-相当于-算法
迭代器-相当于-指针

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

2.4.2 算法:
用有限的步骤,解决逻辑或数学上的问题,这一门学科我们叫做算法(Algorithms)。
算法分为:质变算法非质变算法
质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝,替换,删除等等;
非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等等。

2.4.3 迭代器:
容器和算法之间粘合剂,提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。
注意:每个容器都有自己专属的迭代器
迭代器使用非常类似于指针,初学阶段我们可以先理解迭代器为指针

迭代器的种类:

迭代器名称 用途 特征
输入迭代器 list容器打印函数中有用到 只读,支持++、 == 、!=
输出迭代器 只写,支持++
前向迭代器 读写,并能向前推进迭代器,支持++、==、!=
双向迭代器 list容器 读写,并能向前和向后操作,支持++、–
随机访问迭代器 vector容器deque容器 读写,可以以跳跃的方式访问任意数据,功能最强的迭代器,支持++、–、[n]、-n、<、<=、>、>=
p.s. 常用的容器中迭代器种类为双向迭代器随机访问迭代器

2.5 容器、算法、迭代器初识
STL中最常用的容器为Vector,可以理解为数组

2.5.1 vector存放内置数据类型
重点看几点注释
示例:

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>//①标准算法头文件
using namespace std;

//自定义打印函数 
void MyPrint(int val) {
	cout << val << " ";
}

int main() {

//vector中存内置数据类型
	//②创建vector容器对象,并且通过模板参数来指定容器中存放的数据类型,v1为容器对象名
	vector<int> v1;
	//③用push_back()向容器中添加数据:
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);

	//④每个容器都有自己的迭代器,迭代器用来遍历容器中的元素
	vector<int>::iterator itBegin = v1.begin();//⑤v1.begin()是容器v1的起始迭代器,指向容器v1中第一个数据; itBegin是容器v1的起始迭代器的名称
	vector<int>::iterator itEnd = v1.end();//⑥v1.end()是容器v1的结束迭代器,指向容器v1中最后一个元素的下一个位置; itEnd是容器v1的结束迭代器的名称

	//遍历方式一:
	while (itBegin != itEnd) {
		cout << *itBegin << " ";
		itBegin++;
	}
	cout << endl;
	//遍历方式二:
	for (vector<int>::iterator itera = v1.begin(); itera != v1.end(); itera++) {
		cout << *itera << " ";
	}
	cout << endl;
	//遍历方式三:使用STL提供的标准遍历算法for_each(),头文件是#include<algorithm>
	for_each(v1.begin(),v1.end(),MyPrint);//MyPrint()是自定义的一个打印函数
	cout << endl;

	system("pause");
	return 0;
}

①容器和算法的头文件
②创建vector容器对象,并且通过模板参数来指定容器中存放的数据类型,v1为容器对象名
③用push_back()向容器中添加数据—尾插法push_back()
④每个容器都有自己的迭代器,迭代器用来遍历容器中的元素
v1.begin()是容器v1的起始迭代器,指向容器v1中第一个数据; itBegin是容器v1的起始迭代器的名称
v1.end()是容器v1的结束迭代器,指向容器v1中最后一个元素的下一个位置; itEnd是容器v1的结束迭代器的名称
补充:⑤⑥中的itBegin = v1.begin();itEnd = v1.end();相当于四个指针:itBegin等价于v1.begin() 而itEnd 等价于 v1.end(); 。
⑦遍历方式一和二是一样的,遍历方式三是利用STL提供的标准遍历算法for_each(),还需要自定义一个打印函数MyPrint()
遍历方式二比较简洁一些。

2.5.2 vector存放自定义数据类型
因为itBegin2是指针,所以(*itBegin2)是容器中存放的元素,即Person类,所以要输出类的成员变量,有下面两种方式:
cout << (*itBegin2).name << " " << (*itBegin2).age << endl;//两种输出方式
cout << itBegin2->age << "," << itBegin2->name << endl;//第二种输出方式

示例:

//自定义数据类型Person类
class Person {
public :
	string name;
	int age;

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

int main() {

//vector中存放自定义数据类型Person类
	Person p1("Tom",23);
	Person p2("Jerry",22);
	Person p3("Reus",25);
	//创建容器
	vector<Person> v2;
	//为容器插入数据
	v2.push_back(p1);
	v2.push_back(p2);
	v2.push_back(p3);
	
	vector<Person>::iterator itBegin2 = v2.begin();
	vector<Person>::iterator itEnd2 = v2.end();
	//遍历一
	while (itBegin2 != itEnd2) {
		cout << (*itBegin2).name << " " << (*itBegin2).age << endl;//两种输出方式
		cout << itBegin2->age << "," << itBegin2->name << endl;
		itBegin2++;
	}
	cout << endl;
	//遍历二
	for (vector<Person>::iterator itera2 = v2.begin(); itera2 != v2.end(); itera2++) {
		cout << (*itera2).name << " " << (*itera2).age << endl;
		cout << itera2->age << "," << itera2->name << endl;//两种输出方式
	}
	cout << endl;
	//遍历三
	//for_each(v2.begin(), v2.end(), MyPrint2);
	
//vector中存放自定义数据类型Person类的指针
	Person p4("Tom---", 23);
	Person p5("Jerry---", 22);
	Person p6("Reus---", 25);
	//创建容器
	vector<Person*> v3;//容器中存Person类的指针
	//为容器插入数据
	v3.push_back(&p4);//对象取地址,存入容器中
	v3.push_back(&p5);
	v3.push_back(&p6);

	//vector<Person*>::iterator itBgin3 = v3.begin();
	//vector<Person*>::iterator itEnd3 = v3.end();

	for (vector<Person*>::iterator it = v3.begin(); it != v3.end(); it++) {
		cout << (*(*it)).name << " " << (*(*it)).age << endl;//两种输出方式
		cout << (*it)->name << "," << (*it)->age << endl;
	}


	system("pause");
	return 0;
}

2.5.3 容器嵌套容器
容器相当于一个一维数组,
容器中再嵌套一个数组,就相当于二维数组。

示例:

int main(){

//2.5.3 容器中嵌套容器
	vector< vector<int> > v4;//容器中的元素为vector<int>
	
	//先创建几个元素---容器性质的元素
	vector<int> v11;
	vector<int> v12;
	vector<int> v13;
	vector<int> v14;
	//给几个元素赋值
	for (int i = 0; i < 5; i++) {
		v11.push_back(i);//0 1 2 3 4
		v12.push_back(i + 10);//10 11 12 13 14
		v13.push_back(i + 20);//20 21 22 23 24	
		v14.push_back(i + 30);//30 31 32 33 34
	}
	//将容器元素插入到vector v4中
	v4.push_back(v11);
	v4.push_back(v12);
	v4.push_back(v13);
	v4.push_back(v14);

	//双层for循环输出二维数组:
	for (vector<vector<int>> ::iterator it = v4.begin(); it != v4.end(); it++) {
		for (vector<int>::iterator itera=(*it).begin(); itera !=(*it).end(); itera++) {
			cout << "\t"<< *itera ;
		}
		cout << endl;
	}

	system("pause");
	return 0;
}

解释:
示例中的是4*5的二维数组,
for (vector<vector> ::iterator it = v4.begin(); it != v4.end(); it++) {
for (vector::iterator itera=(*it).begin(); itera !=(*it).end(); itera++) {
cout << “\t”<< *itera ;
}
cout << endl;
}
第一层容器的首元素为it = v4.begin(),各元素为(*it)
所以第二层容器的首元素为itera = (*it).begin(),各元素为(*itera)

3、STL—常用容器

(总)常用容器比较:

容器 | 备注 | 迭代器 | 使用
-------- | ----- | -----|-----|-----| ----- | -----|-----|-----| ----- | -----|-----|-----|-----
string容器 | string类 | | 构造、赋值、拼接、查找、替换、比较、存取、插入、删除
vector容器 | 单端数组 | 随机访问迭代器 | 构造、赋值、容量capacity、大小、插入、删除、存取、交换、预留reserve、排序(#include<algorithm> sort();
deque容器 | 双端数组 | 随机访问迭代器 | 构造、赋值、大小、插入、删除、存取、排序(#include<algorithm> sort();
stack容器(栈) | 栈顶增删,栈底封 | 不能遍历 | 构造、赋值、存取、大小
queue容器(队列) |队尾增,队头删 | 不能遍历 | 构造、赋值、存取、大小
list容器(链表) | 双向循环链表 | 双向迭代器 | 构造、赋值、交换、大小、插入、删除、存取、反转、排序(成员函数:L1.sort();
set容器 | | | 构造、赋值、大小、交换、插入、删除、查找find、统计count、排序(自动排序)
map容器 | <Key,Value> | | 构造、赋值、大小、交换、插入、删除、查找find、统计count、排序(自动排序)

排序的区别见3.8.8最后的总结!!!
打印(打印函数的参数要加引用&):
vector容器和deque容器
(打印方式一:数组的输出方式)

void printDeque_i(deque<int>& d) {
   
	for (int i = 0; i < d.size(); i++) {
   
		cout << d[i] << " ";
	}
	cout << endl;
}

(打印方式二:)

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

stack容器和queue容器:判空,然后挨个出队出栈,打印完容器也就清空了。

void printStack_i(stack<int>& stk) {
   
	
	while (!stk.empty()) {
   
		cout << stk.top() << " ";
		stk.pop();//出栈
	}
	cout << endl;
}

list容器(加了const):

void printList(const list<int>& L) {
   

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

set multiset容器

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

3.1 string容器

构造: string str1;//无参构造 string str2(str1);//拷贝构造
string str3(10, ‘a’);//有参构造
赋值: str2 = str1; str.assign();
拼接: str2 += str1; str.append();
查找: int res = str1.find();//返回所找的元素下标 或 -1 str2.rfind();//倒查
替换: str.replace();
比较: str1.compare(str2);//返回1 0 -1
存取: str[i] 或者 str.at(i)
插入: str.insert()
删除: str.erase()

3.1.1 string基本概念
string是C++风格的字符串,而string本质上是一个类。

string 和 char * :

  • char * 是一个指针
  • string是一个类,类内部封装了char*,管理这个字符串,是一个char*型的容器

特点:
string 类内部封装了很多成员方法,例如:查找find,拷贝copy,删除delete 替换replace,插入insert等。
string管理char*所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责。

3.1.2 string构造函数

string是一个类,所以string str = “Hello,world!”; 中的str就是一个string类的对象名。

示例:

int main() {

	string str1;//调用无参构造函数,创建空字符串
	cout << "str1 = " << str1 << endl;//空

	const char* str = "Hi,Reus.";
	string str2(str);//调用有参构造函数,使用字符串s初始化
	cout << "str2 = " << str2 << endl;//Hi,Reus.
	
    //string str5(const char* s = "Hello,world!");报错!!!
	//cout << "str5 = " << str5 << endl;//

	string str6("Oh!");//调用有参构造函数
	cout << "str6 = " << str6 << endl;//Oh!

	string str7 = "Sorry,";
	cout << "str7 = " << str7 << endl;//Sorry,

	string str3(str2);//调用拷贝构造函数
	cout << "str3 = " << str3 << endl;//Hi,Reus.

	string str4(6, 's');//使用6个字符s初始化
	cout << "str4 = " << str4 << endl;//ssssss

	system("pause");
	return 0;
}

总结:string的多种构造方式没有可比性,灵活使用即可

3.1.3 string赋值操作

1.除了直接用赋值操作符=进行赋值外,
2.还可以利用string类的assign方法进行复制操作。

示例:

int main(){
	string str1;
	str1 = "hello world";
	cout << "str1 = " << str1 << endl;//hello world

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

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

	string str4;
	str4.assign("12345678");
	cout << "str4 = " << str4 << endl;//12345678

	string str5;
	str5.assign("12345678", 5);//把字符串s的前n=5个字符赋给当前的字符串
	cout << "str5 = " << str5 << endl;//12345

	string str6;
	str6.assign(str5);//把字符串s赋给当前的字符串
	cout << "str6 = " << str6 << endl;//12345

	string str7;
	str7.assign(5, 's');//用5个字符x赋给当前字符串
	cout << "str7 = " << str7 << endl;//sssss

	system("pause");
	return 0;

3.1.4 string字符串拼接

1.除了直接用+=操作符进行p拼接外,
2.还可以利用string类的append方法进行拼接操作。

示例:

int main(){

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

	string str3;
	str3.assign("123");//赋值操作
	str3.append(" 654321", 3);//拼接操作; 把字符串s的前n个字符连接到当前字符串结尾
	//str3.append(str2);
	str3.append(str1, 6, 5); // 从下标6位置开始 ,截取5个字符,拼接到字符串str3末尾
	cout << "str3 = " << str3 << endl;//123 65游戏:
	//我 爱 玩 游 戏 :LOL DNF  
	//01 23 45 67 89 10 冒号的下标为10
	//一个汉字占两个字节,所以从下标6位置开始 ,截取5个字符,拼接到字符串str3的末尾

	system("pause");
	return 0;
}

补充:
//我爱玩游戏:LOL DNF
//01 23 45 67 89 10 冒号的下标为10
//一个汉字占两个字节,所以从下标6位置开始 ,截取5个字符,所以会把游戏:拼接到字符串str3的末尾

3.1.5 string查找和替换
查找:
str.find()查找是从左往后,str.rfind()从右往左
找到字符串后返回查找的第一个字符位置,找不到返回-1。
替换:
str.replace()在替换时,要指定从哪个位置起,多少个字符,替换成什么样的字符串

示例:

int main(){

	//查找
	string str1 = "abcdefg hijklmn oq rst";
	cout << "str1 = " << str1 << endl;//abcdefg hijklmn oq rst
	int position;//标记位置
	//正着查:
	position = str1.find("rst");//p  q  rst
	//倒着查:
	position = str1.rfind("rst");//p  q  rst
	
	if(position == -1) 
		cout << "未找到" << endl;
	else
		cout << "找到了,要查找的字符下标为 " << position << endl;
	
	//替换
	str1.replace(8, 7, "p654321p");//从下标8开始,替换7个字符,替换成字符串"p654321p",这里的7用处不大,字符串"p654321p"有8个字符
	cout << "str1 = " << str1 << endl;//abcdefg p654321p oq rst

	system("pause");
	return 0;
}

3.1.6 string字符串比较

字符串比较是按字符的ASCII码进行对比:str1.compare(str2);
相等== 返回 0;
大于> 返回 1 ;
小于< 返回 -1。
另外,字符串对比主要是用于比较两个字符串是否相等,判断谁大谁小并没有意义。

示例:

int main(){

	string s1 = "hello";
	string s2 = "aello";

	int ret;//记录比较结果
	ret = s1.compare(s2);

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

	system("pause");
	return 0;
}

3.1.7 string 字符存取

string字符串中单个字符存取有两种方式,利用 [ ]at
char& operator[](int n); //通过str.[i]方式取字符
char& at(int n); //通过str.at(i)方法获取字符
除了利用这两种方式输出单个字符,还可以修改单个字符,总之就是可以利用这两种方式对单个字符进行各种操作。

示例:

int main(){

string str1 = "Hi, Marco Reus. I am your crazy fan.";
	//三种打印方式:
	cout << "str1 = " << str1 << endl;//Hi, Marco Reus. I am your crazy fan.
	for (int i = 0; i < str1.size(); i++) {
		cout << str1[i] << ",";//依次打印单个字符
	}
	cout << endl;
	for (int i = 0; i < str1.size(); i++) {
		cout << str1.at(i) << " ";//依次打印单个字符
	}
	cout << endl;

	//修改单个字符
	str1[0] = 'h';
	str1.at(1) = 'I';
	cout << "str1 = " << str1 << endl;//hI, Marco Reus. I am your crazy fan.

	system("pause");
	return 0;
}

3.1.8 string插入和删除

插入str.insert() //在指定位置插入___
删除str.erase()//从指定位置pos开始删除n个字符

示例:

int main(){

	string str = "hello";
	cout << str << endl;//hello
	
//插入:
	//str.insert(3,"_这是插入的字符串_");//从下标3开始插入一个字符串  结果:hel_这是插入的字符串_lo
	str.insert(3,5,'_');//从下标3开始插入5个下划线字符				  结果:hel_____lo
	cout << str << endl;
//删除:
	str.erase(3,4);//从下标3开始,删除4个字符
	cout << str << endl;//hel_lo

	system("pause");
	return 0;
}

3.1.9 string 子串
string substr(int pos = 0, int n = npos) const;
str.substr(); //返回由从下标pos开始的n个字符组成的字符串

示例:

int main(){

//示例1
	string str1 = "[email protected]";
	int pos = str1.find('@');//pos既是@的下标位置,也是@之前的字符个数
	//从中提取出qq号
	cout << "qq: " << str1.substr(0, pos) << endl;//起始位置为0,一共提取pos个字符

//示例2
	cout << "情侣争吵的时候是这个样子的:\n我说:";
	string str = "上次是我不对,我不该那么冲动,当然啦,你也有不对,你看哈,假如说...";
	cout << str << endl;
	cout << "(女朋友听到的:..." << str.substr(35, 10) << "...)" << endl;//提取10个字符=5个汉字
	cout << "然后女朋友说:什么?你居然说我不对!!!\n然后就没有然后了...\n\n\n" << endl;

	system("pause");
	return 0;
}

3.2 vector容器

构造: vector v1;//无参构造 vector v2(v1);//拷贝构造
vector v3(10, 3);//有参构造
赋值: v2 = v1; v.assign();
容量和大小:v1.capacity() v1.size()
v1.resize()//重置大小 v.empty()//判空
插入: v1.push_back()//尾插 v1.insert()
删除: v1.pop_back()//尾删 v1.erase() v1.clear()//清空
存取: v1[i] 或者 v1.at() v1.front()//首元素 v1.back()//最后一个元素
交换: v1.swap(v2) vector(v).swap(v)//收缩内存空间,其中vector(v)是通过拷贝构造创建的匿名对象
预留: v1.reserve(100000)//这样就只需开辟一次内存

3.2.1 vector基本概念
vector容器
解释上图:
vector容器会把前面封住,只能在容器的后面进行增删操作
front()—容器中第一个元素
back()—容器中最后一个元素
push_back()—尾插法增加数据;
pop_back()—尾删法删除数据;
v.rend() v.end()—rend表示首元素的前一个位置,end表示最后一个元素的下一个位置;
v.begin() v.rbegin()—begin表示首元素的位置,rbegin表示最后一个元素的位置;
insert()—插入数据
erase()—删除数据
比较常用的是v.begin()v.end();

vector数据结构和数组非常相似,也称为单端数组,(单端是因为容器只能从一端back端进行插数据和删数据)。
vector与普通数组区别:数组是静态空间,而vector可以动态扩展
(补充:动态扩展并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝至新空间,释放原空间。)
另外,vector容器的迭代器是支持随机访问的迭代器!!!

3.2.2 vector构造函数

(几种构造方式在2.5.1已经见过了)

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

示例:

//打印
void printVector_i(vector<char>& v) {
	for (vector<char>::iterator it = v.begin(); it != v.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

int main() {

	//默认构造函数   vector<T> v;
	vector<char> v1;
	//尾插法为容器v1插入元素
	for (int i = 0; i < 5; i++) {
		v1.push_back('a' + i);
	}
	printVector_i(v1);//a b c d e

	//拷贝构造函数   vector(const vector &vec);
	vector<char> v2(v1);
	printVector_i(v2);//a b c d e

	//将v(begin(), end())区间中的元素拷贝给本身   vector(v.begin(), v.end());
	vector<char> v3(v1.begin(), v1.end());
	printVector_i(v3);//a b c d e
	
	//构造函数将n个elem拷贝给本身   vector(n, elem); 
	vector<char> v4(8, 's');
	printVector_i(v4);//s s s s s s s s

	system("pause");
	return 0;
}

3.2.3 vector赋值操作

除了用赋值操作符operator=,还可以用assign()

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

示例:

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

int main() {
	vector<int> v1;
	for (int i = 0; i < 5; i++) {
		v1.push_back(i + 1);
	}
	printVector_i(v1);//1 2 3 4 5

	vector<int> v2;
	v2 = v1;//直接用赋值操作符=
	printVector_i(v2);//1 2 3 4 5

	vector<int> v3;
	v3.assign(v1.begin(), v1.end());
	printVector_i(v3);//1 2 3 4 5

	vector<int> v4;
	v4.assign(8, 6);
	printVector_i(v4);//6 6 6 6 6 6 6 6

	system("pause");
	return 0;
}

3.2.4 vector容量和大小(元素个数)

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

看完示例再解释resize()
示例:

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

int main() {
	vector<int> v1;
	for (int i = 0; i < 7; i++)
	{
		v1.push_back(i + 1);
	}
	printVector_i(v1);//1 2 3 4 5 6 7

	//判断容器是否为空
	if (v1.empty()) {
		cout << "容器v1为空!!!" << endl;
	}
	else
		cout << "容器v1非空" << endl;//√
	//容器的容量和大小(元素个数)
	cout << "容器v1的容量为:" << v1.capacity() << "; 容器v1的元素个数为:" << v1.size() << endl;//9 7
	
	//重置容器的大小resize()
	//①如果newSize > capacity,则必有newSize > size,那么capacity变大,size变大;
	v1.resize(15);//使用默认值0填充新位置
	cout << "容器v1的容量为:" << v1.capacity() << "; 容器v1的元素个数为:" << v1.size() << endl;//15 15
	printVector_i(v1);//1 2 3 4 5 6 7 0 0 0 0 0 0 0 0
	//②如果newSize < capacity且newSize < size,那么capacity不变,size变小。
	v1.resize(3);//
	cout << "容器v1的容量为:" << v1.capacity() << "; 容器v1的元素个数为:" << v1.size() << endl;//15 3,这里容量值没有变成3
	printVector_i(v1);//1 2 3
	//③如果newSize < capacity且newSize > size,那么capacity不变,size变大;
	v1.resize(10, 6);//指定新位置的填充值为6
	cout << "容器v1的容量为:" << v1.capacity() << "; 容器v1的元素个数为:" << v1.size() << endl;//15 10
	printVector_i(v1);//1 2 3 6 6 6 6 6 6 6

	system("pause");
	return 0;
}

总结:
1.容量capacity >= 大小size(元素个数);
2.resize重置的是size的值,共有以下三种情况:
①如果newSize > capacity,则必有newSize > size,那么capacity变大,size变大;
②如果newSize < capacity且newSize > size,那么capacity不变,size变大;
③如果newSize < capacity且newSize < size,那么capacity不变,size变小。

3.2.5 vector插入和删除

  • 尾插 — push_back
  • 尾删 — pop_back
  • 插入 — insert (位置迭代器)—参数是指针
  • 删除 — erase (位置迭代器)—参数是指针
  • 清空 — clear

具体解释:

  • 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_i(vector<int>& v) {//char
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {//char
		cout << *it << " ";
	}
	cout << endl;
}

int main() {

	vector<int> v1;
	//尾插
	v1.push_back(10);
	v1.push_back(20);
	v1.push_back(30);
	v1.push_back(40);
	v1.push_back(50);
	printVector_i(v1);//10 20 30 40 50
	//尾删,删除最后一个元素
	v1.pop_back();
	v1.pop_back();
	printVector_i(v1);//10 20 30

	//插入insert
	v1.insert(v1.begin(), 66);//①在首元素的位置插入一个66
	printVector_i(v1);//66 10 20 30
	v1.insert(v1.begin(), 2, 55);//②在首元素的位置插入两个55
	printVector_i(v1);//55 55 66 10 20 30
	//删除erase
	v1.erase(v1.begin());//删除首元素
	printVector_i(v1);//55 66 10 20 30
	
	//两种清空方式
	v1.erase(v1.begin(), v1.end());//删除头和尾之间的所有元素,即清空
	printVector_i(v1);//空
	v1.clear();//清空
	printVector_i(v1);//空

	system("pause");
	return 0;
}

3.2.6 vector数据存取

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

示例:

int main(){
   

	vector<int> v1;
	for (int i = 0; i < 5; i++) {
   
		v1.push_back(i + 1);//尾插
	}
	for (int i = 0; i < v1.size(); i++) {
   
		cout << v1[i] << " ";//operator[]
	}
	cout << endl;
	for (int i = 0; i < v1.size(); i++) {
   
		cout << v1.at(i) << " ";//at()
	}
	cout << endl;

	cout << "容器v1的首元素为 " << v1.front() << endl;//1
	cout << "容器v1的最后一个元素为 " << v1.back() << endl;//5

	system("pause");
	return 0;
}

3.2.7 vector互换容器
swap(vec); // 将vec与本身的元素互换 v1.swap(v2);
除了交换两个容器的内容,还有实际的功能:收缩内存空间

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

int main() {
   

	vector<int> v1;
	for (int i = 0; i < 5; i++) {
   
		v1.push_back(i + 1);//尾插
	}
	printVector_i(v1);//1 2 3 4 5

	vector<int> v2;
	for (int i = 0; i < 5; i++) {
   
		v2.push_back(i + 10);//尾插
	}
	printVector_i(v2);//10 11 12 13 14

	//交换两个容器
	v1.swap(v2);
	printVector_i(v1);//10 11 12 13 14
	printVector_i(v2);//1 2 3 4 5

//实际用途---收缩内存空间:
	vector<int> v3;
	for (int i = 0; i < 1000; i++) {
   
		v3.push_back(i + 1);//尾插
	}
	cout << "容量:" << v3.capacity() << ";大小:" << v3.size() << endl;//1066 1000
	//重置容器的大小size
	v3.resize(5);
	cout << "容量:" << v3.capacity() << ";大小:" << v3.size() << endl;//1066 5

	//收缩内存
	vector<int>(v3).swap(v3);//具体解释见下图
	cout << "容量:" << v3.capacity() << ";大小:" << v3.size() << endl;//5 5 
		
	system("pause");
	return 0;
}

swap()收缩内存空间
程序中 vector<int>(v3).swap(v3); 先利用拷贝构造函数对v3进行拷贝vector<int>(v3)得到一个匿名对象x容器,然后再把容器x和容器v3进行交换,达到收缩内存的效果。

3.2.8 vector预留空间

reserve(int len);//容器预留len个元素长度,预留位置不初始化,元素不可访问。
功能:减少vector在动态扩展容量时的扩展次数,即减少重复开辟新内存的次数。。如果数据量较大,可以一开始利用reserve()预留空间

示例:

int main(){
   

//3.2.8 vector预留空间
	
	vector<int> v1;
	//如果提前已知需要输入的数据量,利用reserve()提前预留空间,这样就只需开辟一次内存
	v1.reserve(100000);//有了这行代码,第193行代码的结果就变成了1 

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

	for (int i = 0; i < 100000; i++) {
   //1000-18次   10000---24次   100000---30次
		v1.push_back(i + 1);//尾插

		if (p != &v1[0]) {
   
			p = &v1[0];
			num++;
		}
	}
	cout << "共开辟内存的次数为:" << num << endl;//30次 如果加入了第180行的预留空间的代码,就只需要开辟1次内存

	system("pause");
	return 0;
}

3.3 deque容器

#include<deque>//头文件别忘了!!!
构造:deque d1;//无参构造 deque d2(d1);//拷贝构造
deque d3(10, 3);//有参构造
赋值:d2 = d1; d.assign();
和vector有区别)大小:
deque没有容量的概念!!d1.size(); d1.resize()//重置大小; d1.empty()//判空
和vector有区别)插入:
d1.push_back()//尾插; d1.push_front()//头插; d1.insert()
删除:d1.pop_back()//尾删 d1.pop_front()// 头插 d1.erase() d1.clear()//清空
存取:d1[i] 或者 d1.at() d1.front()//首元素 d1.back()//最后一个元素
排序:(加头文件#include) sort();//注意:直接是sort(); 不要写成d1.sort();

3.3.1 deque容器基本概念
双端数组,可以对头尾两端进行插入删除操作。

deque容器与vector容器区别:
deque对头部的插入删除速度比vector快;
vector访问元素时的速度会比deque快,这和两者内部实现有关。
请添加图片描述

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

另外,eque容器的迭代器也是支持随机访问的。

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

示例:

//打印
void printDeque_i(deque<int>& d) {
   
	for (int i = 0; i < d.size(); i++) {
   
		cout << d[i] << " ";
	}
	cout << endl;
}
int main() {
   

//3.3.2 deque构造函数

	//无参构造
	deque<int> d1;
	for (int i = 0; i < 5; i++) {
   
		d1.push_back(i + 1);//尾插
	}
	printDeque_i(d1);//1 2 3 4 5
	
	//拷贝构造
	deque<int> d2(d1);
	printDeque_i(d2);//1 2 3 4 5

	//有参构造
	deque<int> d3(8,3);
	printDeque_i(d3);//3 3 3 3 3 3 3 3

	deque<int> d4(d1.begin(), d1.end());
	printDeque_i(d4);//1 2 3 4 5

	system("pause");
	return 0;
}

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

示例:

//打印
void printDeque_i(deque<int>& d) {
   
	for (int i = 0; i < d.size(); i++) {
   
		cout << d[i] << " ";
	}
	cout << endl;
}
int main() {
   
//3.3.3 deque赋值操作
	deque<int> d1;
	for (int i = 0; i < 10; i++)
	{
   
		d1.push_back(i);
	}
	printDeque_i(d1);//0 1 2 3 4 5 6 7 8 9

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

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

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

	system("pause");
	return 0;
}

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

总结:

  • deque没有容量的概念!!!
  • 判断是否为空 — empty
  • 返回元素个数 — size
  • 重新指定个数 — resize

示例:

//打印
void printDeque_i(deque<int>& d) {
   
	for (int i = 0; i < d.size(); i++) {
   
		cout << d[i] << " ";
	}
	cout << endl;
}
int main() {
   
//3.3.4 deque大小(元素个数)操作 
	deque<int> d1;
	for (int i = 0; i < 5; i++) {
   
		d1.push_back(i + 1);
	}
	printDeque_i(d1);//1 2 3 4 5

	if (d1.empty() == -1) {
   
		cout << "容器d1为空!!!" << endl;
	}
	else
		cout << "容器d1不为空" << endl;//√

	cout << "容器d1的大小(元素个数)为: " << d1.size
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值