c++的提高编程
本阶段主要针对c++泛型编程和STL技术作详细讲解,探讨c++更深层的使用
1.模板
1.1模板的概念
模板就是建立通用模具,大大提高复用性。
例如生活中的存照模板,ppt模板
模板的特点:
- 模板不可以吧直接使用,它只是一个框架
- 模板的通用不是万能的
1.2函数的模板
- c++中另一种编程思想叫泛型编程,主要利用的技术就是模板
- c++提供两种模板机制:函数模板和类模板
1.2.1函数模板的语法
函数模板作用:建立一个同样函数,其函数返回值类型类型和形参类型可以不具体指定,用虚拟类型来代表。
语法:
template
函数的声明或者定义
解释:
- template --声明创建模板
- typename --表明其后面的符号是一种数据类型,可以用class代替
- T --通用数据类型参数,名称可以替换,通常为大写字母
#include<iostream>
using namespace std;
//两个整型变量交换
void intswap(int& a, int& b)
{
int temp;
temp = a;
a = b;
b = temp;
}
//两个浮点型数据交换
void doubleswap(double& a, double& b)
{
double temp;
temp = a;
a = b;
b = temp;
}
template<typename T>//声明一个模板,告诉编译器后面代码中紧跟着的T不要报错,T是一个通用的额数据类型
void myswap(T& a, T& b)
{
T temp;
temp = a;
a = b;
b = temp;
}
void test()
{
int a = 10; int b = 20;
//两种方式使用函数模板
//1.自动类型推导
//swap(a, b);
//cout <<a<<" " << b << endl;
//2.显示指定类型
myswap<int>(a, b);
cout << a << " " << b << endl;
}
int main()
{
test();
system("pause");
return 0;
}
总结:
- 函数模板利用关键字template
- 使用函数模板的两种方式:自动类型推导、显示指定类型
- 模板的目的是为了提高复用性,将类型参数化
- 类型参数可以有多个 typename可以替换为class
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n3TmMtJp-1662911759176)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220313103452022.png)]
1.2.2函数模板的注意事项
注意事项:
- 自动类型推导,必须推导出一致的数据类型T
- 模板必须确定出T的数据类型才能使用
#include<iostream>
using namespace std;
template<class T>
void myswap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
//函数模板的注意事项,
//1.自动类型的额推导,必须推导出一致的数据类型T才能使用
void test()
{
int a = 10;
int b = 20;
double c = 10;
myswap(a, b);
//swap(a, c);错误!!!T必须对应的是相同的数据类型
cout << a << endl;
cout << b << endl;
}
//2.函数模板必须要确定出T的数据类型才能使用
template<class T>
void func()
{
cout << "你是大傻子" << endl;
}
void test02()
{
//func();无法调用没有确定出T的数据类型
func<int>();//确定了T的数据类型所以可以调用
}
int main()
{
test();
test02();
system("pause");
}
总结:
使用模板时必须能使其确定通用的数据类型T,并且能够导出一致的数据类型
1.2.3案例:数组的排序
案例描述:
- 封装一个排序的函数,对不同类型的数组进行排序
- 排列规则从大到小,排序算法为选择排序
- 分别用char数组和int数组进行测试
#include<iostream>
using namespace std;
template<class T>
void myswap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}//交换的数值的模板函数
//template <typename T>
//T mylen(T*n)
//{
// int len = sizeof(n) / sizeof(n[0]);
// return len;
//}
void test(char str[],int len)//算法:选择排序
{
for (int i = 0; i < len; i++)
{
int max = i;
for (int j = i+1; j < len; j++)
{
if (str[max] < str[j])
{
max = j;
}
}
if(max!=i)
myswap(str[i], str[max]);
}
}
template<typename T>//提供打印数组的模板
void printarr(T arr, int len)
{
for (int i = 0; i < len; i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
int main()
{
char arr[] = "abc";
int len = sizeof(arr) / sizeof(arr[0]);
test(arr, len);
printarr(arr, len);
system("pause");
}
1.2.4普通函数与函数模板的区别
普通函数与函数模板的区别:
- 普通函数调用时可以发生自动类型转换(隐式转换类型)
- 函数模板调用时,如果利用自动类型推导,不会发生隐式类型的转化
- 如果利用显示指定类型的方式,可以发生隐式类型的转化
#include<iostream>
using namespace std;
template<class T>
int myswap(T a, T b)
{
T temp = a;
a = b;
b = temp;
return a + b;
}//交换的数值的模板函数
void test()
{
int a=10;
char c = 'c';
//myswap(a, c);报错,函数模板自动类型转换不会发生隐式转化
cout << myswap<int >(a,c) << endl;//显示指定类型能发生隐式的转换
//注意引用不能发生隐式类型的转换 因为引用的本质就是一个指针常量
}
int main()
{
test();
system("pause");
}
1.2.5普通函数和函数调用模板的规则
调用规则如下:
- 如果函数模板和普通函数都可以实现,优先调用普通函数
- 可以通过空模板的参数列表来强制调用函数模板
- 函数模板也可以发生重载
- 如果函数模板可以发生更好的匹配优先调用函数模板
#include<iostream>
using namespace std;
template<class T>
void mytest(T a, T b)
{
cout << "函数模板的调用" << endl;
}
template<class T>
void mytest(T a, T b,T c)
{
cout << "函数模板2的调用" << endl;
}
void mytest(int a, int b)
{
cout << "函数的调用" << endl;
}
void test()
{
int a = 1, b = 2, c = 1;
char d = 'd'; char e = 'e';
mytest(a, b); //1.函数模板和普通函数均可以调用 优先调用函数模板
mytest<>(a, b); //2.添加空模板参数列表可以强制调用函数模板
mytest<>(a, b, c); //3.函数模板也能发生重载
mytest(d, e); //4.如果使用模板函数可以产生更好的匹配优先模板函数(此处若是函数则还要发生隐式转化)
}
int main()
{
test();
system("pause");
}
总结:在提供函数模板的前提下,最好不要提供普通函数以免出现二义性
1.2.6模板的局限性
局限性:
模板不是万能的!!
例如:拿两个类相比,函数模板不能直接实现类与类之间的比较
c++为了解决这种问题,提供模板的重载,可以为这些特定的类型提供具体化模板
#include<iostream>
using namespace std;
#include<string>
class Person
{
public:
Person(int age,string name)
{
this->age = age;
this->name = name;
}
public:
int age;
string name;
};
template<typename T>
bool isyes(T& a, T& b)
{
if (a == b)
return true;
else
return false;
}
//实现具体化 显示具体化的原型和定义 以templa开头,通过名称来指出类型 (函数模板的重载)
//具体化优先于常规模板
template<> bool isyes(Person& a, Person &b)
{
if (a.age == b.age&&a.name==b.name)
return true;
else
return false;
}
void test()
{
Person p(18, "小王");
Person p1(20, "小吴");
bool res = isyes(p, p1);
if (res)
cout << "yes" << endl;
else
cout << "no" << endl;
}
int main()
{
test();
system("pause");
}
总结:
- 利用具体化模板,可以解决自定义类型的通用化
- 学习模板不是为了写模板,而是在STL能够运行系统提供的模板
1.3类的模板
1.3.1类模板的语法
类模板的作用:
- 建立一个通用类,类中的成员数据类型可以不具体制定,用一个虚拟的类型来代表
#include<iostream>
using namespace std;
#include<string>
template<class Typename,class Age>//模板参数可以为多个
class Person
{public:
Person(Typename name, Age age)
{
this->age = age;
this->name = name;
}
public:
Typename name;
Age age;
};
void test()
{
string s = "你好啊";
int age = 18;
Person<string, int> p(s, age);//指定类型
cout << p.age << p.name << endl;
}
int main()
{
test();
system("pause");
}
总结:函数模板和类模板十分的相似 在template后面加类 此类称为类模板
1.3.2类模板与函数模板的区别
主要区别有两点:
- 类模板中没有自动类型推导的公式
- 类模板在参数列表中可以有默认参数
#include<iostream>
using namespace std;
template<class Nametype,class Agetype=int>//类模板在参数列表中有默认的参数
class Person
{
public:
Person(Nametype age, Agetype name)
{
this->age = age;
this->
}
public:
Nametype name;
Agetype age;
};
void test()
{
//Person<> p("bs", 188);不能这样子写 类模板中没有自动推导类型
Person<string>p("nishi", 188);//因为指定了第二个参数是一个默认参数 所以此处可以不指定第二个参数的类型
}
int main()
{
test();
system("pause");
}
总结:
- 类模板中只能用显示指定类型的方式
- 类模板的模板参数列表可以有默认参数
1.3.3类模板中函数的创建时机
类模板中的成员函数和普通类中的成员函数创建时机是有区别的:
- 普通类中成员函数一开始就创建
- 类模板成员函数在调用时才创建
#include<iostream>
using namespace std;
class Person1
{
public:
void showperson1()
{
cout << "1" << endl;
}
};
class Person2
{
public:
void showperson2()
{
cout << "1" << endl;
}
};
template <class T>
class Mymoban
{
//类模板中的成员函数 并不是一开始就创建的,而是模板调用时在产生
; public:
T p;
void func1()
{
p.showperson1();
}
void func2()
{
p.showperson2();
}
};
void test()
{
Mymoban<Person1> m;
m.func1();
//m.func2();报错!!指定类型为person1
}
int main()
{
test();
system("pause");
}
总结:类模板成员函数并不是一开始就被创建的,在调用时才被创建
1.3.4类模板对象作函数参数
类模板实例化出的对象,向函数传参的方式,一共有三种传入方式:
- 指定传入类型–直接显示对象的数据类型
- 参数模板化–将对象中的参数变为模板进行传递
- 整个类模板化-- 将这个对象类型模板化进行传递
#include<iostream>
using namespace std;
template <class T1,class T2>
class Person
{
public:
Person(T1 age, T2 name)
{
this->age = agel
this->name = name;
}
void showperson()
{
cout << age << name << endl;
}
public:
T1 age;
T2 name;
};
//1.指定传入类型
void printperson(Person<int,string>&p)
{
p.showperson();
}
//2.参数模板化
template<class T1,class T2>
void printperson2(Person<T1,T2>&p)
{
p.showperson();
}
//3.整个类模板化
template<class T>
void printperson3(T& p)
{
p.showperson();
cout << "T的数据类型是:" << typeid(T).name() << endl;//查看编译器推导出来的类型
}
void test()
{
Person<int ,string> p(19, "雄安王");
printperson(p);
}
int main()
{
test();
system("pause");
}
总结:
- 通过类模板创建对象,可以有三种方式向函数中进行传参
- 比较广泛的是第一种:指定传入类型
1.3.5类模板与继承
当类模板碰到继承的时候,需要注意以下几点:
- 当子类继承父类中的一个模板时,子类在声明的时候,要指出父类中的T类型
- 如果不指定编译器无法给子类分配内存
- 如果想灵活指出父类中的T的类型,子类也需要变模板
#include<iostream>
using namespace std;
template <class T>
class Base
{
public:
T a;
};
class Son :public Base<int>//必须指定一个类型 因为c++要给子类分配内存 必须知道父类中的T的类型才能往下继承
{
};
void test01()
{
Son s;
}
//如果想灵活指定父类中T的 类型,子类也需要变模板
template <class T1,class T2>
class Son2 :public Base<T1>
{
public:
Son2()
{
cout << "T1的类型为" << typeid(T1).name() << endl;
cout << "T2的类型为" << typeid(T2).name() << endl;
}
T2 a;
};
void test02()
{
Son2<int ,char> s1;
}
int main()
{
test01();
test02();
system("pause");
}
总结:灵活变通。
1.3.6类模板成员函数类外实现
#include<iostream>
using namespace std;
template <class T1, class T2>
class Person
{
public:
Person(T1 name, T2 age);
//{
// this->age = age;
// this->name = name;
//}
void showmessage();
//{
// cout << age << endl;
// cout << name << endl;
//}
T1 name;
T2 age;
};
//类外实现 注意比较个普通类的类外实现的区别
template <class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
this->age = age;
this->name = name;
}
template <class T1, class T2>
void Person<T1, T2>::showmessage()
{
cout << age << endl;
cout << name << endl;
}
int main()
{
system("pause");
}
总结:类模板中成员函数类外实现,需要加上模板参数列表。
1.3.7类模板分文件编写
掌握类模板成员函数分文件编写产生的问题及解决方式
问题:
- 类模板中的成员函数创建时机是在调用阶段,导致份文件编写时链接不到
解决:
- 直接包含.cpp文件
- 将声明和实现写在同一个文件中,并更改后缀名为.hpp,hpp是约定的名称并不是强制的,工程上一般采用这种方法
modle.hpp:
#include<iostream>
using namespace std;
template <class T,class T2> //类模板的声明和实现
class Person
{
public:
void outt(T a, T2 b);
public:
T age;
T2 name;
};
template <class T, class T2>
void Person<T, T2>::outt(T a, T2 b)
{
cout << "你好啊" << endl;
}
main.cpp:
#include"modle.hpp"
using namespace std;
int main()
{
Person<int, int> p;
p.outt(10,10);
system("pause");
}
1.3.8类模块与友元
学习目标:
- 掌握类模板配合友元函数的类内和类外实现
全局函数类内实现-直接在类内声明友元即可
全局函数类外实现-需要提前让编译器知道全局函数的存在
#include<iostream>
using namespace std;
#include<string>
//提前让编译器知道Person类的存在
template <class T1,class T2>
class Person;
//类外实现
template <class T1, class T2>
void showperson(Person<T1, T2>& p)//这是一个函数模板的参数
{
cout << "类外实现的名字是:" << p.name << "年龄是:" << p.age;
}
template <class T1,class T2>
class Person
{
//类内的全局函数
// friend void showperson(Person<T1, T2>& p);
//{
// cout << "名字是:" << p.name << "年龄是:" << p.age;
//}
//类外实现全局函数
//如果全局函数是类外实现的话,需要让编译器提前知道这个函数的存在
friend void showperson <>(Person<T1, T2>& p);//加空模板参数实现 因为外面实现的是一个函数模板
public:
Person(T1 name, T2 age)
{
this->age = age;
this->name = name;
}
private:
T1 name;
T2 age;
};
void test()
{
Person<string, int> p("王仁鑫", 18);
showperson(p);
}
int main()
{
test();
system("pause");
}
总结:建议类内实现 编译器好识别,类外实现太过繁杂
2.初识STL!
2.1STL的诞生
- 软件希望建立可以一直复用的模板
- c++面向对象和泛型编程思想,目的就是复用性的提升
- 大多情况下,数据结构和算法都未能拥有一套标准,导致被迫从事大量的工作
- 为了建立数据结构和算法的一套标准,诞生了STL
2.2STL的基本概念
- STL标准模板库(STAND TEMPLATE LIBRARY)
- STL广义上分为:容器 算法 迭代器
- 容器和算法之间通过迭代器相连
- STL几乎所有代码都采用了模板类或者模板函数
2.3STL六大组件
六大组件分别是:容器 算法 迭代器 仿函数 适配器 空间配置器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L3e1SZAq-1662911759178)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220319142952524.png)]
2.4容器、算法、迭代器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SiCDigMv-1662911759178)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220319143131627.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r53AUOLh-1662911759179)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220319143239714.png)]
迭代器的使用十分类似指针
迭代器的种类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7yNl8eBp-1662911759179)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220319143650775.png)]
2.5容器算法迭代器的初识
STL中做常用的容器为vector,可以理解为数组
2.5.1vector存放内置数据类型
容器:vector
算法:for_each
迭代器:vector::iterator
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>//algorrithm是算法的意思
void myprit(int val)
{
cout << val << endl;
}
void test()
{//创建容器对象,并且通过模板参数指定容器中存放的数据类型
vector<int> v;//a
//向容器中放数据
v.push_back(10);
v.push_back(20);
v.push_back(30);
//每一个容器都有自己的迭代器 迭代器用来遍历数组中的元素
//v.begin() 返回的是一个迭代器 这个迭代器指向的是容器中的第一个数据
//v.end() 返回的是一个迭代器 这个迭代器指向的数组中最后一个元素的后一位
//vector<int>::iterator 拿到vector<int>这种容器迭代器的类型
vector<int>::iterator pbegin = v.begin();//iterator 是迭代器的意思
vector<int>::iterator pend = v.end();
//第一种遍历方式
while (pbegin != pend)
{
cout << *pbegin << endl;
pbegin++;
}
//第二种遍历方式
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << endl;
}
//第三种遍历方式
//使用stl提供的标准遍历算法 头文件algorithm
for_each(v.begin(), v.end(), myprit);//写一个函数名 回调
}
int main()
{
test();
system("pause");
}
2.5.2vector存放自定义数据类型
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>//algorrithm是算法的意思
class Person
{
public:
Person(int age,string name)
{
this->age = age;
this->name = name;
}
public:
int age;
string name;
};
void test()
{
Person p1(18, "小王");
Person p2(19, "小王");
Person p3(12, "小王");
Person p4(14, "小王");
vector<Person*>v;
v.push_back(&p1);
v.push_back(&p2);
v.push_back(&p3);
v.push_back(&p4);
for(vector<Person*>::iterator t=v.begin();t!=v.end();t++)//t可以看作指向存储元素的指针
{
cout << "名字是" << (*t)->name << "年龄是:" << (*t)->age << endl;
}
}
void test02()
{
Person p1(18, "小王");
Person p2(19, "小王");
Person p3(12, "小王");
Person p4(14, "小王");
vector<Person>v;
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
for (vector<Person>::iterator t = v.begin(); t != v.end(); t++)
{
cout << "名字是" << t->name << "年龄是:" << t->age << endl;
}
}
int main()
{
test02();
test();
system("pause");
}
2.5.3vector容器中嵌套容器
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>//algorrithm是算法的意思
void test()
{
vector<vector<int>> v;
vector<int> v1;
vector<int> v2;
vector<int> v3;
vector<int> v4;
for (int i = 0; i < 5; i++)
{
v1.push_back(i);
v2.push_back(i+1);
v3.push_back(i+2);
v4.push_back(i+3);
}
v.push_back(v1);
v.push_back(v2);
v.push_back(v3);
v.push_back(v4);
for (vector<vector<int>>::iterator it = v.begin(); it != v.end(); it++)//*it指的是vector<int>容器 此处可以理解为二维数组
{
for (vector<int>::iterator vit = (*it).begin(); vit != (*it).end(); vit++)//每一个容器都有对应的迭代器
{
cout << *vit << " ";
}
cout << endl;
}
}
int main()
{
test();
system("pause");
}
3.STL常用容器
3.1string
3.1.1string的基本概念
本质:
string的本质是一个类
string和char*的区别:
- char*是一个字符指针
- string是一个类, 类内封装了char**,管理这个字符串,是一个char*的容器
特点:
string类内封装了很多成员的方法
例如:find查找,copy复制,删除delete,替换replace,插入insert
string管理char*分配的内存,不用担心越界的问题,由类内部进行负责
3.1.2string的构造函数
构造函数原型:
- string();创建一个空字符串 例如:string str;
- string(const char* s);//使用字符串s初始化
- string(const string&str);拷贝构造 使用一个string对象初始化另一个string对象
- string(int n,char c) 使用n个字符c初始化
#include<iostream>
using namespace std;
#include<vector>
#include<string>
#include<algorithm>//algorrithm是算法的意思
//- string(); 创建一个空字符串 例如:string str;
//- string(const char* s);//使用字符串s初始化
//-string(const string & str); 拷贝构造 使用一个string对象初始化另一个string对象
//- string(int n, char c) 使用n个字符c初始化
void test()
{
string str;
const char* s = "sbhajdb";
string s1(s);
cout << s1 << endl;
string s2(s1);
cout << s2 << endl;
string s3(5, 'c');
cout << s3 << endl;
}
int main()
{
test();
system("pause");
}
总结:灵活使用!
3.1.3string的赋值操作
赋值函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FnQdHTFT-1662911759180)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220320102955999.png)]
#include<iostream>
using namespace std;
#include<string>
void test()
{
string str = "hellow";
string str1 = str;
string str2 = "c";
string str3;
str3.assign("bshadb");
string str4;
str4.assign("hellow c++", 5);
string str5;
str5.assign(str4);
string str6;
str6.assign(5, 'c');
cout << str6 << endl;
}
int main()
{
test();
system("pause");
}
3.1.4string的拼接操作
函数模型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CkQ6LmN8-1662911759180)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220320140019585.png)]
#include<iostream>
using namespace std;
#include<string>
void test()
{
string str = "i love ";
string str2 = "game";
string str3=" ";
str3 += str += str2;
str3 += 'd';
str3 += " LOL";
cout << str3<< endl;
string str4=" ";
str4.append("你好啊");
str4.append("美女啊",2);
string str5 = "一起玩游戏吗";
str4.append(str5);
string str6 = "球球了";
str4.append(str6, 0, 3);//参数二是你想从哪个位置开始截取 参数三是截取的字符个数
cout << str4 << endl;
}
int main()
{
test();
system("pause");
}
注意:
-
字符串字面值并不是string对象
-
进行+运算时+号两侧的对象至少有一个是string
3.1.5string的查找和替换的操作
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OcmWppPk-1662911759181)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220320143339102.png)]
#include<iostream>
using namespace std;
#include<string>
//查找
void test()
{
string a="abcdefa";
int pos=a.find("cd");
cout << pos << endl;
pos = a.find('s', 4);
cout << pos << endl;
string b = "f";
pos = a.find(b);
cout << pos << endl;
//find和rfind的区别是 一个是从左往右找 一个是从右往左找
pos=a.rfind('a');
cout << pos << endl;
pos = a.find('a');
cout << pos << endl;
}
//替换
void test2()
{
string str;
str = "abcda";
str.replace(1, 1, "bbbbbb");//第一个位置的字符开始后的一个字符 替换为”bbbbbb“
cout <<str << endl;
}
int main()
{
test();
test2();
system("pause");
}
3.1.6字符串的比较
比较方式
- 字符串的比较是按照字符的acsll码进行对比的
等于返回0
大于返回1
小于返回-1
#include<iostream>
using namespace std;
#include<string>
//查找
void test()
{
string str = "hellow";
string str2 = "aellow";
int res=str.compare(str2);
cout << res << endl;
}
int main()
{
test();
system("pause");
}
3.1.7字符串的存取
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DizL8eSf-1662911759181)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220320151541962.png)]
#include<iostream>
using namespace std;
#include<string>
void test()
{
string s = "hellow";
for (int i = 0; i < s.size(); i++) //size()函数返回字符串的大小、
{
cout << s[i];
}
cout << endl;
for (int i = 0; i < s.size(); i++) //size()函数返回字符串的大小、
{
cout << s.at(i);
}
cout << endl;
//修改
s[2] = 'a';
s.at(3) = 'a';
}
int main()
{
test();
system("pause");
}
总结:string单个字符存取方式有两种 一个是[]一个是at()
3.1.8字符串的插入和删除
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VKjsZtuv-1662911759182)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220320155616919.png)]
#include<iostream>
using namespace std;
#include<string>
void test()
{
string s = "hellow";
s.insert(1, "111");
cout << s << endl;
string s2 = "123";
s.insert(3, s2);
cout << s << endl;
s.erase(1, 3);//删除从第一个位置开始 三个字符
cout << s << endl;
}
int main()
{
test();
system("pause");
}
总结:c++数组起始下标是0
3.1.9字符串的子串
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-456IrXyr-1662911759182)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220320160129160.png)]
#include<iostream>
using namespace std;
#include<string>
void test()
{
string s = "hellow";
string s2=s.substr(0, 5);
cout << s2 << endl;
}
//实例
void test02()
{
string qq = "jdbasjbd@qq.com";
qq.find('@');
string qq_name = qq.substr(0, qq.find('@'));//从第零个位置开始 qq.find('@')个字符组成的字符串
cout << qq_name << endl;
}
int main()
{
test();
test02();
system("pause");
}
3.2vector容器
3.2.1vector容器的基本概念
功能:
- vector数据结构和数组十分相似,也成为单端数组
vector和普通数组的区别:
- 不同之处在于数组是静态空间,而vector可以动态扩展(通过关键字)
动态扩展:
- 并不是在原空间之后续新空间,而是找更大的内存空间,然后将原数据拷贝新空间,释放原空间
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iOvOZhqz-1662911759183)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220322193734838.png)]
- vector容器迭代器是支持随机访问的迭代器
3.2.2vector构造函数
功能描述:
- 创建vector容器
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QLXjPyZ0-1662911759183)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220322194159961.png)]
#include<iostream>
#include<vector>
using namespace std;
void P_vector(vector<int>(&v))
{
for(vector<int>::iterator it=v.begin();it!=v.end();it++)
{
cout<<*it<<" ";
}
}
int main()
{
vector<int>v;
v.push_back(10);
v.push_back((20));
v.push_back((30));
vector<int>::iterator it;
for(it=v.begin();it!=v.end();it++)
{
cout<<*it<<endl;
}
vector<int>v1(v.begin(),v.end());
P_vector(v1);
vector<int>v2(v1);
P_vector(v2);
vector<int>v3(5,0);
system("pause");
return 0;
}
3.2.3vector容器的赋值操作
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DaLIhrTx-1662911759184)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220322200934896.png)]
#include<iostream>
#include<vector>
using namespace std;
void P_vector(vector<int>(v))
{
for(vector<int>::iterator it=v.begin();it!=v.end();it++)
{
cout<<*it<<" ";
}
}
int main()
{
vector<int>v;
v.push_back(10);
v.push_back((20));
v.push_back((30));
vector<int>v2=v;
P_vector(v);
vector<int>v3;
v3.assign(3,100);
P_vector(v3);
vector<int>v4;
v4.assign(v.begin(),v.end());
P_vector(v4);
return 0;
}
3.2.4vector的容量和大小
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YwxL8YqJ-1662911759184)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220322202219743.png)]
#include<iostream>
#include<vector>
using namespace std;
void P_vector(vector<int>(v))
{
for(vector<int>::iterator it=v.begin();it!=v.end();it++)
{
cout<<*it<<" ";
}
}
int main()
{
vector<int>v;
v.push_back(10);
v.push_back((20));
v.push_back((30));
if(v.empty()) {
cout << "yes" << endl;
}
else {
cout << "\n" << v.capacity() << endl;
cout << v.size() << endl;
v.resize(2,0);//重新指定size的大小 并且多余的用0来补充 若比原来的改变的size比原来的小 不会改变capacity的大小
cout<<v.capacity()<<endl;
P_vector(v);
}
return 0;
}
3.2.5vector的插入和删除
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WWncbSrd-1662911759185)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220322204859731.png)]
#include<iostream>
#include<vector>
using namespace std;
void P_vector(vector<int>(v))
{
for(vector<int>::iterator it=v.begin();it!=v.end();it++)
{
cout<<*it<<" ";
}
}
int main()
{
vector<int>v;
//尾插法
v.push_back(10);
v.push_back((20));
v.push_back((30));
P_vector(v);
//尾删法
v.pop_back();
v.pop_back();
P_vector(v);
//插入
v.insert(v.begin(),100);
P_vector(v);
v.insert(v.begin(),5,100);
P_vector(v);
//删除
v.erase(v.begin());
P_vector(v);
v.erase(v.begin(),v.end());//删除所有元素
//删除所有元素
v.clear();
return 0;
}
3.2.6vector的数据存取
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lc1TfJeJ-1662911759185)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220322210417143.png)]
int main()
{
vector<int>v;
//尾插法
v.push_back(10);
v.push_back((20));
v.push_back((30));
//通过下标访问
v[0]=11;
//通过at访问
v.at(0)=12;
//返回第一个元素
v.front();
//返回最后一个元素
v.back();
return 0;
}
3.2.7vector互换容器
功能:
- 实现两个容器元素进行交换
函数原型:
- swap(v);//将v与本身的元素互换
#include<iostream>
#include<vector>
using namespace std;
void P_vector(vector<int>(v))
{
for(vector<int>::iterator it=v.begin();it!=v.end();it++)
{
cout<<*it<<" ";
}
}
int main()
{
//交换两个容器的元素
vector<int>v;
v.push_back(10);
v.push_back((20));
v.push_back((30));
vector<int>v1;
v1.push_back(30);
v1.push_back(20);
v1.push_back(10);
v.swap(v1);
P_vector(v);
//利用swap来收缩内存
vector<int>v3(10000);
cout<<v3.size()<<" "<<v3.capacity()<<endl;
v3.resize(3);
vector<int>(v3).swap(v3);//vector<int>(v3) 是一个调用拷贝构造拷贝v3的匿名对象(v3已经改变大小),用匿名对象与v3做交换,匿名对象作用完这一行就会消失
cout<<v3.size()<<" "<<v3.capacity();//此时v3和匿名对象已经交换了 大小已经改变了
return 0;
}
总结:
swap能来交换两个容器的元素,切利用匿名对象来收缩内存。交换容器会改变原来的大小和容量
3.2.8利用reserve预留空间
功能描述:
- 减少vector在动态扩展容量时的扩展次数
函数原型:
- reserver(int len);//容器预留len个元素长度,预留位置不初始化,元素不可访问。
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> v;
v.reserve(10000);//预留空间 减少v在自动扩展容量时的次数
int num=0;
int *p= nullptr;
for(int i=0;i<10000;i++)
{
v.push_back(i);
if(p!=&v[0])
{
num++;//记录容量扩展的次数
p=&v[0];
}
}
cout<<num<<endl;
return 0;
}
总结:如果数据量够大可以在一开始就预留空间。
3.3deque容器
3.3.1deque的基本概念
功能:
- 双端数组,可以对头端进行插入删除操作
deque与vector的区别:
- vector对于头部插入的删除效率低,数据量越大效率越低
- deque相对而言,对头部的插入和删除速度比vector快
- vector访问元素的时候速度会比deque快,这和两者的内部实现有关
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kRQjeX9C-1662911759186)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220326114712007.png)]
deque的工作原理:
deque内部有个中控器,维护每段缓存区的内容,缓冲区中存放真实数据,中控器维护的时每个缓冲区的地址,使得使用deque时像一片连续的内存空间。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q5QjQ3LE-1662911759186)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220326114914975.png)]
- deque支持随机访问。
3.3.2构造函数
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EkTYHvtR-1662911759186)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220326115514521.png)]
#include<iostream>
#include<deque>
using namespace std;
void printfde(const deque<int> &d )//顶层const不允许函数内修改
{
for(deque<int>::const_iterator it=d.begin();it!=d.end();it++)
{
cout<<*it<<endl;
}
}
int main()
{
deque<int> d; //默认构造
d.push_back(10);
d.push_back(20);
printfde(d);
deque<int>d1(d.begin(),d.end());//传入迭代器区间的有参构造函数
printfde((d1));
deque<int>d3(5,100); //指定n个数的有参构造
printfde(d3);
deque<int>d4(d3); //拷贝构造
printfde(d4);
return 0;
}
3.3.3deque的赋值方式
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-diMRP9tO-1662911759187)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220326132217595.png)]
#include<iostream>
#include<deque>
using namespace std;
void printfde(const deque<int> &d )
{
for(deque<int>::const_iterator it=d.begin();it!=d.end();it++)
{
cout<<*it<<endl;
}
}
int main()
{
deque<int> d;//默认构造
d.push_back(10);
d.push_back(20);
deque<int>d2=d;
printfde(d2);
deque<int>d3;
d3.assign(d2.begin(),d2.end());
printfde(d3);
deque<int>d4;
d4.assign(4,100);
printfde(d4);
return 0;
}
3.3.4deque的大小操作
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MeplFW4A-1662911759187)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220326132649324.png)]
#include<iostream>
#include<deque>
using namespace std;
void printfde(const deque<int> &d )
{
for(deque<int>::const_iterator it=d.begin();it!=d.end();it++)
{
cout<<*it<<endl;
}
}
int main()
{
deque<int> d;//默认构造
d.push_back(10);
d.push_back(20);
d.push_back(10);
d.push_back(20);
d.push_back(10);
d.push_back(20);
if(d.empty())
return 0;
else
{
cout<<"不会为空"<<endl;
cout<<d.size()<<endl;//没有容量的概念 与本身的结构有关系
printfde(d);
d.resize(10,0);
printfde(d);
d.resize(3);
printfde(d);
}
return 0;
}
3.3.5deque的插入和删除数据
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rmVMwcCe-1662911759187)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220326135405448.png)]
#include<iostream>
#include<deque>
using namespace std;
void printfde(const deque<int> &d )
{
for(deque<int>::const_iterator it=d.begin();it!=d.end();it++)
{
cout<<*it<<" ";
}
cout<<endl;
}
int main()
{
deque<int> d;//默认构造
//两端插入删除操作
d.push_back(10);
d.push_back(20);
d.push_front(1);
d.push_front(2);
d.pop_back();
d.pop_front();
printfde(d);
//insert插入
deque<int>d2;
d2.insert(d2.begin(),1);
d2.insert(++d2.begin(),5,2);
printfde(d2);
d2.insert(d2.begin(),d2.begin(),d2.end());
printfde(d2);
//erase删除操作
d2.erase(d2.begin());
printfde(d2);
d2.erase(d2.begin(),d2.end());
printfde(d2);
//清空操作
d2.clear();
return 0;
}
3.3.6deque数据存取
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eBvCzWhO-1662911759188)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220326140335778.png)]
#include<iostream>
#include<deque>
using namespace std;
int main()
{
deque<int> d;//默认构造
//两端插入删除操作
d.push_back(10);
d.push_back(20);
d.push_front(1);
d.push_front(2);
cout<<d[0]<<endl;
cout<<d.at(1)<<endl;
cout<<d.front()<<endl;
cout<<d.back()<<endl;
return 0;
}
3.3.7deque的排序
功能描述:
利用算法实现对deque容器进行排序
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-erFf5HCc-1662911759188)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220326140833332.png)]
3.4评委打分案例
简单说明:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oI072mPP-1662911759188)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220327014452152.png)]
#include<iostream>
#include<string>
#include<deque>
#include<vector>
#include<algorithm>
#include<ctime>
using namespace std;
//选手的类
class Person
{
public:
Person(string name, int score) :name(name), score(score)
{
}
public:
string name;
int score;
};
//创建选手
void creatPerson(vector<Person>&v)
{
string nameseed = "ABCDE";
for (int i=0;i<5;i++)
{
string name = "选手";
int score = 0;
name += nameseed[i];
Person p(name, score);
v.push_back(p);
}
}
//打分
void setscoreAndshow(vector<Person>& v)
{
for (vector<Person>::iterator it = v.begin(); it != v.end(); it++)
{
deque<int>d;//将评委分数放入deque中
for (int i = 0; i < 10; i++)
{
int score = rand() % 41 + 60;//60-100的随机数
d.push_back(score);
}
//进行排序 升序
sort(d.begin(), d.end());
//删掉最高分和最d
d.pop_back();
d.pop_front();
int sum = 0;
for (deque<int>::iterator dt=d.begin(); dt != d.end(); dt++)
{
sum += *dt;
}
int everage = sum / d.size();
(*it).score = everage;
cout << "选手:" << it->name << " " << "分数:" << it->score << endl;
}
}
int main()
{
//随机数种子
srand((unsigned int)time(NULL));
vector<Person>v;
creatPerson(v);
setscoreAndshow(v);
system("pause");
}
3.5stack容器
3.5.1栈的基本概念
概念:栈是一个后进先出的数据结构,它只有一个出口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-85dwKV51-1662911759189)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220327095407586.png)]
注意:
- 栈中只有顶部元素可以被外界访问到,因此栈不允许有遍历的行为。
- 数据的出入称为 出栈和入栈。
3.5.2stack的常用接口
功能:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GJW1RxAi-1662911759189)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220327095721910.png)]
#include<iostream>
#include<stack>
using namespace std;
int main()
{
stack<int>s;
s.push(10);
s.push(23);
s.push(30);
s.push(40);
while(!s.empty())
{
cout<<s.size()<<endl;
cout<<s.top()<<endl;
s.pop();
}
stack<int>s2(s);
stack<int>s3=s2;
return 0;
}
3.6queue容器
3.6.1queue的基本概念
概念:
队列是一个先进先出的数据结构,他有两个出口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7e0x6xHm-1662911759190)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220327101842646.png)]
注意:
- 队列只允许在一端新增数据,在另一端删除数据
- 队列中只有队头和队尾才能被外界使用,因此队列不允许有遍历的行为
- 数据的出入叫进入队和出队
3.6.2队列的常用接口
功能:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mahvX66Q-1662911759190)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220327102449873.png)]
#include<iostream>
#include<queue>
#include<string>
using namespace std;
class Person
{
public:
Person(string name, int age) {
this->name = name;
this->age = age;
}
public:
string name;
int age;
};
int main()
{
Person p1("唐僧", 30);
Person p2("孙悟空", 60);
Person p3("猪八戒", 40);
Person p4("沙僧", 90);
queue<Person>q;
q.push(p1);
q.push(p2);
q.push(p3);
q.push(p4);
while (!q.empty())//先进先出 后进后出
{
//访问队头
cout << q.front().name << " " << q.front().age<<endl;
//访问对尾
cout << q.back().name << " " << q.back().age<<endl;
q.pop();
}
cout << q.size() << endl;
system("pause");
}
3.7list容器
3.7.1list的基本概念
功能:
将数据进行链式存储
链表的组成:链表由一系列结点组成
节点的组成:一个存储数据元素的数据域,另一个是存储数据的指针域
STL中的链表是一个双向链表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mFO8rzeo-1662911759191)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220328220416741.png)]
优缺点:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j4XrqueS-1662911759191)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220328220629813.png)]
3.7.2list的构造函数
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eERrLIoa-1662911759191)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220328234903917.png)]
#include<iostream>
#include<list>
#include<string>
using namespace std;
void printfg(list<int>&l)
{
for(list<int>::iterator it=l.begin();it!=l.end();it++)
{
cout<<*it<<" ";
}
cout<<endl;
}
void test01()
{
list <int >l;//默认构造
l.push_back(10);
l.push_back((20));
list<int>l2(l);//拷贝构造
list<int>l3(l.begin(),l.end());//将区间的元素拷贝给本身
list<int>l4(5,100);//将n个value拷贝给本身
printfg(l);
printfg(l2);
printfg(l3);
printfg(l4);
}
int main()
{
test01();
}
3.7.3list的赋值和交换
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZybdeVwA-1662911759192)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220328235908847.png)]
#include<iostream>
#include<list>
#include<string>
using namespace std;
void printfg(list<int>&l)
{
for(list<int>::iterator it=l.begin();it!=l.end();it++)
{
cout<<*it<<" ";
}
cout<<endl;
}
void test01()
{
list <int >l;//默认构造
l.push_back(10);
l.push_back((20));
printfg(l);
list<int>l2=l;
printfg(l2);
list<int>l3;
l3.assign(l.begin(),l.end());
printfg(l3);
l3.assign(8,100);
printfg(l3);
l3.swap(l2);
printfg(l3);
}
int main()
{
test01();
}
3.7.4list的大小操作
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L9mO6d2O-1662911759192)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220329134309689.png)]
#include<iostream>
#include<list>
#include<string>
using namespace std;
void printfl(list<int>&l)
{
for(list<int>::iterator it=l.begin();it!=l.end();it++)
{
cout<<*it<<" ";
}
cout<<endl;
}
int main()
{
list<int>l;
l.push_back(10);
l.push_back(29);
l.push_back(30);
printfl(l);
cout<<l.size()<<endl;
if(!l.empty())
{
l.resize(2);
printfl(l);
l.resize(8,1000);
printfl(l);
}
return 0;
}
3.7.5list的插入和删除
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LpLgkC0D-1662911759193)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220329135433135.png)]
3.7.6list的数据存取
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zcAciDri-1662911759193)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220329135533911.png)]
3.7.7list的反转和排序
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FUVPLSFM-1662911759193)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220329144100953.png)]
#include<iostream>
#include<list>
#include<string>
using namespace std;
bool myconpare(int v1,int v2)//z
{//降序就让第一个数>第二个数
return v1>v2;
}
void printfl(list<int>&l)
{
for(list<int>::iterator it=l.begin();it!=l.end();it++)
{
cout<<*it<<" ";
}
cout<<endl;
}
int main()
{
list<int>l;
l.push_back(10);
l.push_back(29);
l.push_back(20);
printfl(l);
l.reverse();//反转
printfl(l);
/* sort(l.begin(),l.end());//会报错 不支持随机访问的迭代器不能使用标准算法*/
l.sort();//这是容器内部成员函数自定义的sort函数 从小到大升序
printfl(l);
l.sort(myconpare);//传入自己设置的降序函数名s之变为降序
printfl(l);
return 0;
}
3.7.8list的排序案例
#include<iostream>
#include<list>
#include<string>
using namespace std;
class Person
{
public:
Person(string name, int age, int height) :name(name), age(age), height(height)
{
}
public:
string name;
int age;
int height;
};
void printfl(list<Person>& l)
{
for (list<Person>::iterator it = l.begin(); it != l.end(); it++)
{
cout << (*it).name << " " << (*it).age << " " << (*it).height << endl;
}
cout << endl;
}
bool mysort(Person& p, Person& p1)//回调函数 自定义类型排序的设定
{
if (p.age == p1.age)
return p.height < p1.height;
return p.age < p1.age;
}
int main()
{
list<Person>l;
Person p1("A", 15, 188);
Person p2("B", 119, 180);
Person p3("C", 16, 189);
Person p4("D", 16, 180);
printfl(l);
l.push_back(p1);
l.push_back(p2);
l.push_back(p3);
l.push_back(p4);
l.sort(mysort);
printfl(l);
system("pause");
}
3.8set/multiset容器
3.8.1set的基本概念
简介:
- 所有元素都会在插入时自动排序
本质:
- set/multiset属于关联式容器,底层结构是二叉树实现的(红黑树)
set和multiset的区别:
- set不允许容器中由重复的元素
- multise允许容器中由重复的元素
3.8.2set构造和赋值
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-unFYmtqa-1662911759194)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220412210439416.png)]
#include<iostream>
#include<set>
using namespace std;
void Printf(set<int>&s)
{
for(auto i=s.begin();i!=s.end();++i)
{
std::cout<<*i<<" ";
}
cout<<endl;
}
int main()
{
set<int>s;
//set容器只有insert这一种插入数据的方式
s.insert(20);
s.insert(30);
s.insert(10);
s.insert(30);
//set容器不允许插入重复值
//set容器的特点所有元素插入时自动排序
Printf(s);
set<int>s2(s);
Printf(s2);
set<int>s3;
s3=s2;
Printf(s3);
}
3.8.3set容器大小和交换
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NdjUBpV5-1662911759194)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220412212441445.png)]
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XoSRkAZJ-1662911759194)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220412212630364.png)]
3.8.4set容器的查找和统计
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yaUBsJHu-1662911759195)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220412212827305.png)]
#include<iostream>
#include<set>
using namespace std;
int main()
{
set<int>s;
//set容器只有insert这一种插入数据的方式
s.insert(20);
s.insert(30);
s.insert(10);
//查找元素
auto t=s.find(20);
if(t!=s.end())
{
cout<<"找到啦"<<*t<<endl;
}
else
{
cout<<"没有找到"<<endl;
}
//同级元素
auto y=s.count(20);//返回0或者1
if(y)
{
cout<<"找到了"<<endl;
}
else
{
cout<<"没有找到"<<endl;
}
}
3.8.6set和multiset的区别
- set不可以插入重复的值,multiset可以
- set插入数据的同时会返回插入的结果,表示插入是否成功
- multiset不会检测数据因此可以重复插入数据
#include<iostream>
#include<set>
using namespace std;
int main()
{
//不能插入重复的数
set<int>s;
//set容器只有insert这一种插入数据的方式
s.insert(20);
auto res=s.insert(30);//set的insert返回是一个系统自带的类模板下的类类型(队组) 第一个参数是容器的迭代器,第二个参数是bool类型,是否插入成功决定返回值
pair<set<int>::iterator,bool>res2=s.insert(10);
if(res.second)
{
cout<<*(res.first);
}
if(res2.second)
{
cout<<*res2.first;
}
//能插入重复的数
multiset<int>ms;
auto res3=ms.insert(10);
multiset<int>::iterator res4=ms.insert(20);//multiset的insert返回值是一个迭代器
cout<<*res3<<*res4;
}
总结:
- 运行重复插入数据用multiset不允许用set
3.8.7pair的使用-pair队组的创建
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-we2p7tvt-1662911759195)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220415151501704.png)]
#include<iostream>
#include<set>
#include <string>
using namespace std;
void test()
{
//第一种方式
pair<string,int>p("你好",5);
cout<<p.first<<p.second;
//第二种方式
pair<string,int>p2= make_pair("欢饮",8);
cout<<p2.first<<p2.second;
}
int main()
{
test();
}
3.8.8set容器排序
- set容器的默认排序是从小到大 利用仿函数可以改变排序规则
对于内置数据类型:
#include<iostream>
#include<set>
using namespace std;
class Compare//仿函数重载()符号
{
public:
bool operator()(int v1,int v2)
{
return v1>v2;//降序 顺序和传进来的参数顺序有关
}
};
void printf(set<int,Compare>&s)
{
for(auto p=s.begin();p!=s.end();p++)
{
cout<<*p<<" ";//指针是迭代器的一种
}
}
void test()
{
set<int,Compare>s;
s.insert(1);
s.insert(4);
s.insert(6);
s.insert(8);
printf(s);
}
int main()
{
test();
}
对于自定义数据类型:
#include<iostream>
#include<set>
#include<string>
using namespace std;
class Person
{
public:
int age;
string name;
Person(int age,string name)
{
this->age=age;
this->name=name;
}
};
class Compare//仿函数重载()符号
{
public:
bool operator()(const Person p1,const Person p2)
{
return p1.age>p2.age;//降序 顺序和传进来的参数顺序有关
}
};
void printf(set<Person,Compare>&s)
{
for(auto p=s.begin();p!=s.end();p++)
{
cout<<p->name<<" "<<p->age<<endl;//指针是迭代器的一种
}
}
void test()
{
set<Person,Compare>s;
Person p1(3,"小王");
Person p2(8,"小吴");
Person p3(1,"小陈");
s.insert(p1);
s.insert(p2);
s.insert(p3);
printf(s);
}
int main()
{
test();
}
3.9map和multimap容器
3.9.1map的基本概念
简介:
- map中所有元素都是pair
- pair中的第一个元素为key(键值)起到索引的作用,第二个元素为value(实值)
- 所有的元素都会根据元素的键值自动排序
本质:
- 本质是由二叉树实现的
优点:
- 可以根据key值快速找到value
map和mutinap的区别:
- map不允许有相同的key值
- mutimap运行容器有相同的key值
3.9.2map的构造和赋值
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bNdnayit-1662911759196)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220416092015203.png)]
#include<iostream>
#include<map>
void printf(std::map<int,int>&m)
{
for(std::map<int,int>::iterator it=m.begin();it!=m.end();++it)
{
std::cout<<(*it).first<<" "<<(*it).second<<std::endl;
}
std::cout<<std::endl;
}
int main()
{
std::map<int,int>m;
m.insert(std::pair<int,int>(1,1));
m.insert(std::pair<int,int>(2,2));
m.insert(std::pair<int,int>(3,3));
printf(m);
std::map<int,int>m1(m);
printf(m1);
std::map<int,int>m2;
m2=m1;
printf(m2);
std::cout<<sizeof(m);//存放元素的总字节数
}
3.9.3map的大小和交换
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-chWITtaa-1662911759197)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220416093555601.png)]
注意:大小返回的是队组的个数或者说是value值的个数
3.9.4map的插入和删除
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2xkURMz1-1662911759197)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220416093853943.png)]
#include<iostream>
#include<map>
using namespace std;
void printf(std::map<int,int>&m)
{
for(std::map<int,int>::iterator it=m.begin();it!=m.end();++it)
{
std::cout<<(*it).first<<" "<<(*it).second<<std::endl;
}
std::cout<<std::endl;
}
int main()
{
std::map<int,int>m;
//三种插入方式
//第一种
m.insert(pair<int,int>(1,1));//匿名对象
//第二种
m.insert(make_pair(2,2));
//第三种
m.insert(map<int,int>::value_type(3,3));
printf(m);
//删除
m.erase(m.begin());//迭代器指定元素
m.erase(2);//根据键值删除
m.erase(m.cbegin(),m.cend());//指定区间删除 c表示const
//清空
m.clear();
}
3.9.5查找和统计
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Oar1cxnD-1662911759198)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220416102444854.png)]
注意:map容器重载了[]可以利用其键值找到对应的元素 []中填键值
3.9.6map容器的排序操作
- map的容器默认情况下按照key值的大小从小到大进行排序 利用仿函数可以改变排序规则
类似于set容器的排序。
4.1函数对象(仿函数)
函数对象是一个广泛的概念,因为所有具有函数行为的对象都可以称为函数对象。这是一个高级抽象,我们不关心对象到底是什么,只要其具有函数行为即可。函数行为是指可以使用()
调用并传递参数
4.1.1函数对象的基本概念
概念:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RkmWsmBI-1662911759198)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220419210237754.png)]
4.1.2函数对象的使用
特点:
- 函数对象在使用的时候可以像普通函数那样调用,可以有参数,可以有返回值
- 函数对象超出普通函数的概念,函数对象可以有自己的状态
- 函数对象可以作为参数传递
#include<iostream>
#include <string>
using namespace std;
class Myfunc
{
public:
Myfunc(string s):s(s),count(0){}
void operator()(string str)
{
cout<<s<<" "<<str<<endl;
count++;
}
public:
string s;
int count;
};
//1.当作普通函数使用
void test01()
{
Myfunc m("你好啊");
m("中国");
}
//2.函数对象可以有自己的状态
void test02()
{
Myfunc m1("你好啊");
m1("中国");
m1("中国");
m1("中国");
m1("中国");
cout<<"被调用的次数为"<<m1.count<<endl;
}
//3.函数对象可以作为参数传递
void dowork(Myfunc &m,string test)
{
m(test);
}
void test03()
{
Myfunc m3("你好啊");
dowork(m3,"中国");
}
int main()
{
test01();
test02();
test03();
}
4.2谓词
4.2.1一元谓词
概念:
- 返回bool类型的仿函数称为谓词
- 如果operator()接受一个参数为一元谓词
- 如果operator()接受两个参数为二元谓词
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
class Mycmp
{
public:
bool operator()(int val)
{
return val>5;
}
};
int main()
{
vector<int>v;
for(int i=0;i<7;i++)
{
v.push_back(i);
}
//找出元素大于5的数
auto x=find_if(v.begin(),v.end(),Mycmp());//传参是传对象 所以这里传一个匿名对象
if(x==v.end())
cout<<"没有找到"<<endl;
else
cout<<"找到了是"<<*x<<endl;
}
4.2.2二元谓词
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
class Mycmp
{
public:
bool operator()(int v1,int v2)
{
return v1>v2;
}
};
int main()
{
vector<int>v;
v.push_back(10);
v.push_back(12);
v.push_back(12);
v.push_back(11);
v.push_back(15);
sort(v.begin(),v.end(),Mycmp());//匿名对象
for(int i=0;i<5;++i)
{
cout<<v[i]<<" ";
}
}
4.3内建函数对象
4.3.1内建函数对象的意义
概念:STL内建了一些函数对象
分类:
- 算术仿函数
- 关系仿函数
- 逻辑仿函数
用法:
- 这些仿函数所产生的对象,用法和一般函数完全相同
- 使用内建函数对象,需要引入头文件 #include
4.3.2算术仿函数
功能描述:
- 实现四则运算
- 其中negate是一元运算,其他都是二元运算
仿函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Eq6Zbtz9-1662911759199)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220420162000605.png)]
#include<iostream>
using namespace std;
#include<functional>
void test()
{
negate<int>n;//取反
cout<<n(10)<<endl;
plus<int>m;//加法
m(10,20);
cout<<m(10,20)<<endl;
}
int main()
{
test();
return 0;
}
4.3.3关系仿函数
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-piw0dQFz-1662911759199)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220420164333200.png)]
#include <iostream>
#include<algorithm>
#include<vector>
#include <functional>
using namespace std;
int main()
{
vector<int>v;
v.push_back(20);
v.push_back(90);
v.push_back(30);
v.push_back(40);
v.push_back(60);
//Stl自建仿函数 大于仿函数
sort(v.begin(),v.end(), greater<int>());//创建了一个匿名对象
for(auto x=v.begin();x!=v.end();x++)
{
cout<<*x<<" ";
}
return 0;
}
4.3.4逻辑仿函数
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2ecv4iCx-1662911759200)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220425201122498.png)]
#include <iostream>
#include<algorithm>
#include<vector>
#include <functional>
using namespace std;
int main()
{
vector<bool>v;
v.push_back(true);
v.push_back(false);
v.push_back(true);
v.push_back(false);
v.push_back(true);
for(auto x=v.begin();x!=v.end();x++)
{
cout<<*x<<" ";
}
cout<<endl;
//将容器取非装入另一个容器
vector<bool>v2;
v2.resize(v.size());
transform(v.begin(),v.end(),v2.begin(),logical_not<bool>());//最后一个参数是仿函数,创建一个匿名对象
//transform 是一个搬运函数
for(auto x=v2.begin();x!=v2.end();x++)
{
cout<<*x<<" ";
}
//将容
return 0;
}
5.STL中常用的算法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JIaCnxty-1662911759200)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220427184532505.png)]
5.1常用遍历算法
5.1.1for_each算法
5.1.2transform算法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4aCnGLaK-1662911759201)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220427190646281.png)]
#include<iostream>
#include<algorithm>
#include<vector>
class Mytarget
{
public:
int operator()(int a)
{
return a;
}
};
class Myout
{
public:
void operator()(int val)
{
std::cout<<val<<" ";
}
};
using namespace std;
int main()
{
vector<int>v;
v.push_back(10);
v.push_back(9);
v.push_back(8);
v.push_back(7);
vector<int>tagetv(5);
transform(v.begin(),v.end(),tagetv.begin(),Mytarget());//遍历容器且搬运数据 从一个容器到另一个容器
for_each(tagetv.begin(),tagetv.end(),Myout());
}
5.2常用的查找算法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RZFN4Ftt-1662911759201)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220427191537478.png)]
5.2.1find
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ej2MrU4x-1662911759201)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220427191828711.png)]
#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
using namespace std;
//内置数据类型
void test01()
{
vector<int>v;
v.push_back(10);
v.push_back(9);
v.push_back(8);
v.push_back(7);
auto pos=find(v.begin(),v.end(),5);
if(pos!=v.end())
{
cout<<"找到了"<<endl;
}
else
{
cout<<"没有找到"<<endl;
}
}
//自定义数据类型
class Person
{
public:
Person(string name,int age)
{
this->age=age;
this->name=name;
}
bool operator==(const Person& p)//根据find的底层重载==号
{
if(this->name==p.name&& this->age==p.age)
{
return true;
}
else
{
return false;
}
}
int age;
string name;
};
void test02()
{
Person p("a",18);
Person p1("b",19);
Person p2("c",17);
Person p3("d",16);
vector<Person>v;
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p);
Person p4("b",19);
auto x=find(v.begin(),v.end(),p4);
if(x!=v.end())
{
cout<<"找到了"<<endl;
}
else
{
cout<<"没有找到"<<endl;
}
}
int main()
{
test01();
test02();
}
5.2.2find_if(按条件查找)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rxfIzlVI-1662911759202)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220427194721590.png)]
#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
using namespace std;
class Person
{
public:
Person(string name,int age)
{
this->age=age;
this->name=name;
}
int age;
string name;
};
//内置类型
class Mycmp
{
public:
bool operator()(int val)
{
return val>5;//一元谓词
}
bool operator()(const Person&p)
{
return p.age>20;//一元谓词
}
};
void test01()
{
vector<int>v;
v.push_back(10);
v.push_back(9);
v.push_back(8);
v.push_back(7);
auto pos=find_if(v.begin(),v.end(),Mycmp());//满足条件则成立
if(pos!=v.end())
{
cout<<"找到了"<<endl;
}
else
{
cout<<"没有找到"<<endl;
}
}
//自定义数据类型
//class Person
//{
//public:
// Person(string name,int age)
// {
// this->age=age;
// this->name=name;
// }
//
// int age;
// string name;
//};
void test02()
{
Person p("a",18);
Person p1("b",19);
Person p2("c",17);
Person p3("d",16);
vector<Person>v;
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p);
Person p4("b",19);
auto x=find_if(v.begin(),v.end(),Mycmp());
if(x!=v.end())
{
cout<<"找到了"<<endl;
}
else
{
cout<<"没有找到"<<endl;
}
}
int main()
{
test01();
test02();
}
5.2.3adjacent_find
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ItqVYFrh-1662911759202)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220427201934402.png)]
5.2.4binary_search(二分查找)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KOS8MXAb-1662911759203)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220427202405342.png)]
find和binary_search的区别
- 返回值不一样 find返回的是迭代器,binary_search返回的是bool类型
- find有序无序均可以查找 binary_search只能用于有序序列中
- binary_search查找速度很高
5.2.5count
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cfh0dSAw-1662911759203)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220427202919243.png)]
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
void test01()
{
vector<int>v;
v.push_back(10);
v.push_back(10);
v.push_back(10);
v.push_back(10);
cout<<"10的个数为:"<<count(v.begin(),v.end(),10);
}
int main()
{
test01();
}
5.2.6count_if(按条件查找)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nsZJPsk5-1662911759204)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220427205227054.png)]
5.3常用的排序算法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3YA833n4-1662911759204)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220427205407718.png)]
5.3.1sort排序
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SYcvLVgD-1662911759204)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220427205440717.png)]
注意:第三个参数可填可以不填 如果填的话填函数名或者谓词都行
5.3.2random_shuffle
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tj5ScXey-1662911759205)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220427205605061.png)]
#include<iostream>
#include<algorithm>
#include<vector>
#include <ctime>
using namespace std;
void Myrrint(int val)
{
cout<<val<<" ";
}
void test01()
{
//随机数的种子
srand(static_cast<unsigned >(time(NULL)));//根据时间的变化是每一次打乱的顺序不一样
vector<int>v;
v.push_back(10);
v.push_back(50);
v.push_back(30);
v.push_back(2);
random_shuffle(v.begin(),v.end());
for_each(v.begin(),v.end(), Myrrint);
}
int main()
{
test01();
}
5.3.3merge
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UoMYLqmr-1662911759205)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220427213935538.png)]
注意:必须是有序的!!!
5.3.4reverse
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RE6h4dyx-1662911759206)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220427214147339.png)]
注意:跟vector容器的resrrver预留空间成员函数要区分开来
5.4常用的拷贝和替换算法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nxeXBjUc-1662911759206)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220427214356276.png)]
5.4.1copy
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-24VsvnVf-1662911759206)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220427214428510.png)]
注意;目标容器要提前开辟空间
5.4.2replace
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eAt48YFu-1662911759207)(C:\Users\28108\AppData\Roaming\Typora\typora-user-images\image-20220427214713451.png)]