0831
C++提高编程:
1、模板
2、初识STL
2、初识STL
2.1 诞生
为提升代码的复用性,C++提供了面向对象编程思想
和泛型编程思想
。为了建立数据结构
和算法
的一套标准,诞生了STL
。
面向对象的编程思想:封装、继承、多态
泛型编程思想:模板Template
2.2 STL基本概念
STL(Standard Template Library,标准模板库)。
另外,STL 几乎所有的代码都采用了模板类
或者模板函数
,见函数模板的总结。
2.3 STL六大组件
六大组件为:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器。主要学习前四大组件
。
- 容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据。
- 算法:各种常用的算法,如sort、find、copy、for_each等
- 迭代器:扮演了容器与算法之间的胶合剂。
- 仿函数:行为类似函数,可作为算法的某种策略。见6.函数调用运算符()重载—仿函数 —3.7.7、3.7.8、3.8.8也有涉及到仿函数
- 适配器:一种用来修饰容器或者仿函数或迭代器接口的东西。
- 空间配置器:负责空间的配置与管理。
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容器会把前面封住,只能在容器的后面进行增删操作
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);
//尾部插入元素elepop_back();
//删除最后一个元素insert(const_iterator pos, ele);
//①迭代器指向位置pos插入元素eleinsert(const_iterator pos, int count,ele);
//②迭代器指向位置pos插入count个元素eleerase(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;
}
程序中 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时像一片连续的内存空间。
另外,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