通过前面的C++内容整理,对于C++有了一些了解,但是每次应用的时候都会无从下手,《21天学通C++》这本书对于增长自己C++的自信心还是挺有帮助的,推荐学习下这本书,前面的知识较基础,所以就从16章开始整理了,前面的内容也要过一遍
先看我前面的笔记对C++的学习,再从这个书里稍微放松下,笔记里对于一些内容做出注释,可以同时参考书和笔记同时学。
目录
在list的头部或者尾部插入元素 push_front()、 push_back()
在 STL map 和 multimap 中插入元素 insert
在 STL map 或 multimap 中查找元素 find
迭代器
迭代器是指针,指向容器的第一个元素,可以递增指向下一个元素。在对有const修饰的对象时,必须使用const_iterator
迭代器总共有四种:正向迭代器(iterator)、反向迭代器(reverse_iterator)、只读迭代器(const_iterator)、只读反向迭代器(const_reverse_iterator)
使用方法:容器名::选择的迭代器 迭代器名;
string::iterator it;
迭代器的接口
begin()--第一个元素的位置、rbegin()、cbegin()、crbegin()
begin():指向容器第一个元素的位置(可读可写)
rbegin():指向容器最后一个元素的位置(可读可写)
cbegin():指向容器第一个元素的位置(只读)
crbegin():指向容器最后一个元素的位置(只读)
end()--最后一个元素的下一个位置、rend()、cend()、crend()
end():指向容器最后一个元素的下一个位置(可读可写)
rend():指向容器第一个元素的前一个位置(可读可写)
cend():指向容器最后一个元素的下一个位置(只读)
crend():指向容器第一个元素的前一个位置(只读)
16 STL string类
实例化和复制STL string
加上const关键字才可以将常量赋予变量,因为const会创建一个临时变量来存储常量值,之后将这个常量赋予给变量。
可以让string在构造时只接受传入字符串的前N个字符(需要传入的参数为常量字符串或者C风格字符串,如果传入string则是接受传入数后面的字符串);也可以初始化为包含特定数量的指定字符。
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
int main()
{
const char* Cstring = "hello";
string strform(Cstring); //构造函数
cout << strform;
//string复制
string str2("hello world");
string str3(str2);
cout << "str3:" << str3 << endl;
//复制前几个字符串
string part(Cstring, 3);
cout << "part:" << part << endl;
//重复
string chongfu(10, 'a');
cout << "重复的字符串chongfu:" << chongfu << endl;
}
c风格的字符串互相复制必须使用strcpy函数。
访问string的字符串内容
访问string字符串的内容可以使用迭代器,也可以使用数组下标的形式,想要获得string的C风格表示,需要使用成员函数c_str()。string类的字符串length函数可以计算长度,输出的时候使用下标索引。
在使用迭代器时注意不要越界。 迭代器在使用时使用auto根据后面的begin函数来自动推导
拼接字符串 +=或append()
可以使用+=(实际上是string类重载了+=运算符)或者append()来完成字符串的拼接
append函数重载了多个,除了可以接受string还可以接收C风格的字符串。
在string中查找字符或者字符子串 find
string的成员函数find可以在给定string中查找字符或者字符串。返回值是查找的索引(下标从0开始),指向查找字符的偏移量,没有找到则返回npos,find(目标字符串,开始查找的位置)
string s, c;
int main() {
s = "apple";
c = "l";
int index = s.find(c);
if (index != string::npos)
cout << index << endl;
}
rfind也是一种查找方法,不过是从字符串最后开始查找,返回下标位置(正向数的下标)
string s = "apple";
cout << s.rfind("l") << endl;
截断STL string erase
STL string类提供erase函数
erase(删除起始位置,删除的长度)
使用迭代器——erase(删除起始的迭代器位置,删除尾的迭代器位置)
erase(删除迭代器位置)
#include <iostream> // 包含头文件。
#include<algorithm>
#include<string>
int main()
{
string str2("hello world");
cout << str2.erase(0, 4)<<endl; //删除索引区间的字符
const char* str="0123456";
string str3(str);
str3.erase(str3.begin() + 1);
cout << str3;
str3.erase(str3.begin() + 1, str3.end() - 1);
cout << str3;
}
字符串反转 reverse
reverse函数可以实现字符串的反转
reverse(反转的起始位置,反转的终值位置)
#include <iostream> // 包含头文件。
#include<algorithm>
#include<string>
int main()
{
const char* str="0123456";
string str3(str);
reverse(str3.begin(), str3.end());
cout << str3;
}
字符串的大小写转换 transform
transform第一个参数是变化的迭代器起始地址,第二个是变化的迭代器的末地址,第三个参数是变化结果存放的迭代器位置,最后一个是判断大写转换还是小写转换
#include <iostream> // 包含头文件。
#include<algorithm>
#include<string>
using namespace std; // 指定缺省的命名空间。
int main()
{
string str3="abcjfa";
transform(str3.begin(), str3.end(), str3.begin(), ::toupper);
cout << str3<<endl;
transform(str3.begin()+1, str3.end()-3, str3.begin(), ::tolower);
cout << str3;
}
17 STL动态数组类
动态数组vector的特点
当不知道需要存储多少元素的时候就考虑动态数组vector(只能在尾部插入删除)或者deque(头部和尾部都可以插入删除)
在末尾插入和删除元素所用时间相等
数组中间插入或者删除元素与后面的元素个数成正比
存储的元素数是动态的,vector负责内存管理
动态数组vector的操作
实例化vector
实例化动态数组需要指定存储的元素类型
第三个是实例化一个Tuua类的vector。
实例化时直接指定元素的内容; 初始化一个10个元素的动态数组; 初始化一个10个元素的动态数组,数组的元素值为90; 通过一个动态数组创建一个数组; 从另一个数组的部分元素创建一个动态数组。
使用push_back()在末尾插入元素
数组名.push_back(插入的元素值) 数组名.size()返回数组的元素个数
列表初始化
使用insert()在指定位置插入元素
在指定位置插入元素 数组名.insert(插入位置,插入元素)
在指定位置插入多个元素 数组名.insert(插入位置,插入元素个数,插入元素值)
在指定位置插入另一个数组的元素 数组名.insert(插入位置,另一个数组起始位置,另一个数组末位置)
使用数组语法访问vector的元素
使用下标运算法[]; 使用成员函数at() 数组名.at(索引值); 使用迭代器
使用指针语法访问vector的元素
迭代器就是指针,可以递增递减指向上一个或者下一个元素,也可以解引用。
迭代器的声明直接用auto推导 auto 迭代器名 = vector名.begin()
distance()函数用于计算两个迭代器直接的元素个数
vector<int>arr{10,20,30,40}; //大括号用于初始化元素值
cout<<distance(arr.begin, arr.end);
删除vector的元素pop_back()
pop_back ()删除vector末尾的元素。
size()可以用于求vector的元素个数 vector名.size()
理解大小和容量
大小size 指的是目前存储的元素个数 STL的容器类都可以用size计算元素个数
容量capacity 指的是vector在重新动态分配内存以存储更多的元素 大小<=容量
STL deque类
另一种动态数组,但是支持在头部和尾部插入或者删除元素
在尾部插入或者删除元素,push_back pop_back
在头部插入或者删除元素,push_front pop_front
18 STL list和forward_list
list双向链表--插入和删除速度快
在使用时要包含头文件#include<list>
基本的list操作
实例化list
只指定list存储的元素数,不输入内容,list类会自动将其置空,不用再手动初始化。 对于list的元素访问的方式,只能通过迭代器指针解引用
int main()
{
list<int>list1(10); //生成一个10个元素的空列表
for (auto count = list1.begin(); count != list1.end(); count++)
cout << *count << endl;
return 0;
}
在list的头部或者尾部插入元素 push_front()、 push_back()
相关的操作和deque相似,push_front()和push_back()
int main()
{
list<int>list1(10); //生成一个10个元素的空列表
for (auto count = list1.begin(); count != list1.end(); count++)
cout << *count << " ";
list1.push_back(30);
list1.push_front(30);
cout << endl;
for (auto count = list1.begin(); count != list1.end(); count++)
cout << *count << " ";
return 0;
}
将容器的元素输出可以从main函数拿出来定义一个函数
template<typename T>
void show(const T& contain) //抽象为容器,其他的容器也可以输出
{
for (auto count = contain.begin(); count != contain.end(); count++)
cout << *count << ' ';
}
//使用auto关键字可以避免容器前面的声明,这样可以泛化容器
在list中间插入元素 insert()
insert函数在使用时只能往头部或者尾部插入元素,与push_front和 push_back不同的是insert可以一次性插入多个。
在往另一个list中插入另一个list时只能将全部元素插入,迭代器的位置只能是首尾。
删除list中间的元素erase
删除元素有两个版本,一个是删除迭代器指向位置的元素,另一个是删除迭代器指向区间的元素。
对于容器的清空,最好的方法是调用clear函数
对list的元素进行排序和反转
利用reverse进行反转
没有传入参数,直接在容器后使用函数
对元素进行排序sort
两种排序策略
第一种是不传入参数,直接进行排序(升序) 容器名.sort
第二种是接受一个二元函数作为参数,降序排列
下面的函数向编译器解释什么是小,从而让编译器理解降序
对包含对象的list进行排序以及删除其中的元素
如果list包含一个类,这个类中有多个对象,要求按照一个对象的大小进行排列,有以下两种办法:
1 在list包含的对象类中重载 < 运算符
2 提供一个排序二元谓词函数,接收两个输入值,返回一个bool值,从而判断那个比那个小
erase(搭配迭代器) remove(搭配值) 删除容器中所有查找的值
forward_list单向链表
单向链表只能通过一个方向遍历
使用单向链表需要添加头文件#include<forward_list>
在向单向链表插入元素时只能使用push_front,不能使用push_back,但是可以使用insert()向其中插入元素。
当需要频繁的删除或者插入元素时(尤其是在中间进行插入删除)应使用list
当容器中包含类时,需要重载 < 和 == 运算符,这样可以进行排序操作。
容器的清空 容器名.clear() 容器包含的元素个数 容器名.size()
19 STL集合类
插入和查找的时间是固定的。元素插入时将对其进行排序,频繁查找时考虑集合
容器set和multiset可以让程序员在容器中快速查找键,键是存储在一位容器中的值。set和multiset的区别就是set只能存放唯一的值,而multiset可以存放相同的值。在容器中最好默认迭代器为const_iterator,不要通过迭代器修改指向元素的值
STL set 和 multiset 的基本操作
实例化set对象
因为 set 和 multiset 在元素插入时都会对元素进行排序,如果没有指定排列顺序将按照升序排列
因为二元排序谓词在创建时使用模版template,所以在使用时要指明使用的数据类型。
实例化的三种方式:只声明数据类型; 声明数据类型和排序方式; 通过其他实例来创建
最后一种实例化方式也可以使用其他的STL容器,只要能够用begin和end描述边界就行。
在 set 或 multiset中插入元素 insert
向 set 和 multiset 插入元素时,编译器都会对元素进行排序,默认按照升序排列,如果想实现降序,需要自己定义二元谓词函数。插入元素时会按照大小排到对应的位置,set中不能出现重复元素,multiset中可以存在多个相同的元素。
multiset可以使用count返回查找元素的次数 multiset名.count()
在 set 或 multiset中查找元素 find
因为multiset中可以包含多个相同值的元素,在使用find时将匹配第一个元素,如果找到该元素就返回其索引;如果没有找到这个元素就返回集合最后一个元素的索引。
15行使用一个计数器来判断是否得到结果,如果返回的索引值和end()函数的结果相同则说明检索完成没有找到
删除 set 或 multiset 中的元素 erase
在使用erase前务必使用 .count 来查看有多少个元素包含特定的值
erase的几种方式:直接删除值 删除迭代器的索引(用find获得索引) 删除两个迭代器之间
erase 将删除multiset中所有对应的值
使用迭代器删除排序小于所有查找的值
set的实际用例
将对象存储在 set 或者 multiset 等容器的类中时,一定要重载实现 < 和 == 两个运算符,前面将成为排序谓词,后者将用来实现 .find 等函数
使用 STL set 和 multiset 的优缺点
20 STL映射类
频繁而快速搜索的应用程序使用
map只能存储唯一的键,而multimap可以存储多个相同的键。
STL map 和 multimap的基本操作
实例化 STL map 和 multimap
实例化一个键为整数,值为字符串的map,< >内部有三个参数,第一个是键的类型,第二个是值的类型,第三个是选择的排序方式,不选定的话默认升序排列。
实例化的三种方式:只声明键和值的数据类型; 声明键和值的数据类型和排序方式; 通过其他实例来创建(直接将其他实例作为参数传入、使用迭代器)
在 STL map 和 multimap 中插入元素 insert
在使用insert插入时,需要对键值对的数据类型进行说明。make_pair可以直接传递键值对
其中键和值的输出可以用迭代器指针来表明,键的指向为first,值的指向为second。
map 和 multimap 在使用 insert 时都会默认升序排列
#include <iostream>
using namespace std;
#include<map>
template <typename T>
void show(const T& contain)
{
for (auto count = contain.cbegin(); count!=contain.cend();count++)
{
cout<<count->first<<";"<<count->second<<endl;
}
}
int main()
{
map<int,string>map1;
map1.insert(pair<int,string>(10,"shuju"));
map1.insert(make_pair(20,"leixing"));
show(map1);
return 0;
}
在 STL map 或 multimap 中查找元素 find
find 通过给定的键查找值,返回值为一个迭代器。先判断迭代器是否查找到值,再进行输出
map 和 multimap 在使用 find 时都会默认升序排列
在 STL multimap 中查找元素
因为multimap可以包含多个相同的键的键值对,multimap::count可以确定多少个值与键对应,再通过迭代器递增访问这些临近值。
multimap名.count(查找的键) 返回值为multimap中有多少相同键的键值对
删除 STL map 或 multimap 中的元素
erase的几种方式:直接删除值 删除迭代器的索引(用find获得索引) 删除两个迭代器范围之间的键值对
提供自定义的排序谓词
multimap::count(key)可以指出在multimap中有多少元素的键为key
find()函数的返回值先于end()进行检查再使用