一、模板
1.概念:
使用模板可以让程序员编写与类型无关的代码,专注于算法设计,程序运行的时候可以是任何类型,共用一套算法。
模板是泛型编程的基础,泛型编程的目的是发明一种语言机制,能够帮助实现一个通用的标准模板库STL。
2.分类:
函数模板和类模板。
2.1 函数模板
使用函数模板可以确保算法的通用性,不同类型都可以调用函数,不过算法也要确保兼容性,打印语句print传入对象肯定是不合适的,直接打印对象不合适。
#include <iostream>
using namespace std;
template <class T> //可以使用class也可以使用typename,没有本质区别
T add(T a,T b){
return a+b;
}
template <typename T,typename Q>
void print(const T &a,const Q &b){ //第一次使用函数就确定传入的类型
cout << a << " " << b << endl;
}
int main (){
cout << add(1,2) << endl;
cout << add(1.2,3.3) << endl;
int a = 9;
string b = "adada";
print(a,b);
return 0;
}
2.2 类模板
不确定类的成员变量以及返回值类型可以使用。
#include <iostream>
using namespace std;
template <class T,class Q> //创建两个模板
class Animal{
private:
T value;
Q name;
public:
Animal(T value,Q name):value(value),name(name){}
T get_value(){ //定义对象的时候确定的第一种类型
return value;
}
Q get_name(){ //定义对象的时候确定的第二种类型
return name;
}
};
int main (){
Animal<int,string> a(4,"asdad"); //类指定变量类型,指定了类型后,这个对象的成员变量的类型就固定,别的对象创建可以单独指定类型
cout << a.get_value() << " " << a.get_name() << endl;
Animal<string,double> a1("zhangsan",12.5); //a1是一个新对象,可以重新指定类型
cout << a1.get_value() << " " << a1.get_name() << endl;
return 0;
}
可以类内声明类外定义
#include <iostream>
using namespace std;
template <class T,class Q> //创建两个模板
class Animal{
private:
T value;
Q name;
public:
Animal(T value,Q name):value(value),name(name){}
T get_value(); //定义对象的时候确定的第一种类型
Q get_name(); //定义对象的时候确定的第二种类型
};
template <class T,class Q> //类外定义,类型必须一致
T Animal<T,Q>::get_value(){ //返回值类型和指定Animal类型保持与类的返回值和变量类型一致
return value;
}
template <class T,class Q>
Q Animal<T,Q>::get_name(){
return name;
}
int main (){
Animal<int,string> a(4,"asdad"); //类指定变量类型,指定了类型后,这个对象的成员变量的类型就固定,别的对象创建可以单独指定类型
cout << a.get_value() << " " << a.get_name() << endl;
Animal<string,double> a1("zhangsan",12.5); //a1是一个新对象,可以重新指定类型
cout << a1.get_value() << " " << a1.get_name() << endl;
return 0;
}
二、容器
1.标准模板库 STL
STL标准模板库是惠普实验室开发的一系列软件的统称。在应用于C++前已经存在很长时间。
STL 是 C++ 标准库的重要组成部分,是一套通用的、可复用的类和函数模板,用于数据结构和算法的实现。STL 让 C++ 程序员可以高效、方便地处理各种数据集合。
STL 的三大核心(广义上):容器(重要)、算法、迭代器
2.容器 container
2.1 概念:
C++ 容器(container)是 C++ 标准模板库(STL, Standard Template Library)中非常重要的组成部分。容器用于存储和管理数据集合。
容器类自动申请和释放聂村,无需new和delete操作。所有的容器类的使用都要引入对应的头文件,因为这些类型都是std名字空间下的类。
容器(Containers)有三种类型:顺序容器(重要),关联容器(重要),无序关联容器
顺序容器分为:string字符串(之前讲过),array数组,vector向量,list列表,deque队列(自行扩展练习,和别的容器类似)
2.2 顺序容器
是一种各元素之间有顺序关系的线性表,是一种线性结构的有序群集,按照插入顺序排序。
2.2.1 array数组
C++11引入array类型代替传统数组。相比于内置的传统数组,array更加安全和易于使用。
#include <iostream>
#include <array>
using namespace std;
int main (){
array<int,4> arr = {1,2,3,4}; //定义int类型数组,长度为4
arr[0] = 11; //可以使用[]取内容
arr.at(1) = 22; //和string一样可以使用.at()取内容
for(auto &i:arr){ //循环遍历for-each
cout << i << endl;
}
arr.fill(-1); //给所有元素赋一个统一的值
for(auto &i:arr){
cout << i << endl;
}
return 0;
}
2.2.2 vector向量
vector相比array是长度是动态可变的,支持插入插入删除等操作,内部使用数组实现,所有元素的空间地址是连续的,所以随机存取的效率很高,插入删除的效率很低。
#include <iostream>
#include <vector>
using namespace std;
int main (){
vector<int> vec(4); //初始化所有的元素都是0
cout << "数组长度:" <<vec.size() << endl; //.size函数求长度
vec[0] = 11; //具备数组的性质,[]取元素
vec.at(1) = 22; //也可以使用.at()函数
for(auto &i:vec){ //循环遍历
cout << i << " ";
}
cout << endl;
vec.insert(vec.begin()+2,10); //在下标为二的位置插入一个元素,数组长度加一
vec.insert(vec.begin(),100); //在下标为零的位置插入一个元素,数组长度加一
cout << "插入两个元素后数组长度:" <<vec.size() << endl;
for(auto &i:vec){ //循环遍历
cout << i << " ";
}
cout << endl;
vec.clear(); //清空向量容器
cout << "vec向量容器是否为空:" << vec.empty() << endl; //判断当前向量容器是否为空(1是空,0是非空)
for(auto &i:vec){ //循环遍历
cout << i << " ";
}
return 0;
}
2.2.3 list列表(重点)
list列表内部使用双向链表实现,因此list更擅长插入和删除操作,但是由于元素之间的地址不连续,随机存取的能力比较差,也不支持下标访问。(和C语言链表性质类似)
#include <iostream>
#include <list>
using namespace std;
int main (){
list<int> lis(5); //创建长度为5的列表
cout << "初始化链表长度为:" << lis.size() << endl;
//iterator(迭代器)是一种特殊指针,高度封装
list<int>::iterator iter = lis.begin(); //这里让迭代器指向列表第一个元素
*iter = 11; //利用iterator指针修改第一个元素数值
iter ++; //指针后移指向下一个list元素
*iter = 22; //修改列表第二个数值
for(auto i:lis){ //循环遍历列表
cout << i << " ";
}
cout << endl;
lis.push_front(101); //在列表第一个位置添加一个元素
lis.push_back(44); //在列表最后一个为位置添加一个元素
for(auto i:lis){ //循环遍历
cout << i << " ";
}
cout << endl;
cout << *iter << endl; //指针依旧指向数值为22的元素
advance(iter,2); //后移两个位置(可以跳过头结点)
advance(iter,-3); //后移三个位置
cout << *iter << endl;
//.begain()返回的指针指向第一个元素,.end()返回的是最后一个元素后一个元素,可以这么理解,尾节点后面还有一个节点元素和头结点元素值一样,所以.begin()返回的地址第一次--的值还是第一个元素的值,.end()一样。
iter = lis.begin(); //复位,让iter指针指向第一个位置元素
iter --;
cout << *iter << endl;
iter = lis.end(); //让iter指针指向第一个位置后面的元素
iter ++;
cout << *iter << endl;
lis.sort(); //从小到大排序
for(auto i:lis){ //循环遍历
cout << i << " ";
}
cout << endl;
iter = lis.end(); //前面排序后,iter指针指向的链表不存在,要重新复位
advance(iter,-4); //移到倒数第四个元素
lis.erase(iter);
for(auto i:lis){ //循环遍历
cout << i << " ";
}
cout << endl;
lis.clear(); //清空列表
cout << "列表是否为空:" << lis.empty()<<endl;
for(auto i:lis){ //循环遍历
cout << i << " ";
}
return 0;
}
2.2.4 deque队列
deque是一种均衡的容器才,从接口上几乎兼容vector和list的功能,新能介于二者之间。
2.3 关联容器
主要讲解map:键值对映射
关联的元素没有严格的顺序关系,但是内部有排序,因此才可以被迭代器遍历
元素的数据被称为值(value),每个元素拥有一个第一无二的名称:键(key),所以一个完整的元素被称为键值对。
#include <iostream>
#include <map>
using namespace std;
int main (){
map<string,int> map1; //创建一个关联容器
//增加元素
map1["身高"] = {178};
map1.insert(pair<string,int>("月薪",20000));
map1.insert(pair<string,int>("身高",188)); //第一个键是唯一的,第二次插入一样的键不会修改值
//两种方式取出数据
cout << map1["月薪"] << endl;
cout << map1.at("身高") << endl;
//没有该键的时候会创建一个值为零点键
cout << map1["年龄"] << endl;
cout << "关联容器map1长度:" << map1.size() << endl;
//判断键值对是否存在
map<string,int>::iterator iter = map1.find("年龄");
//删除键值对
if(iter == map1.end()) //如果find没找到键返回的是尾节点地址
cout << "无法删除,查无此键!" << endl;
else
map1.erase("年龄");
map1.clear(); //清空关联容器
cout << "关联容器map1长度:" << map1.size() << endl;
cout << "关联容器map1是否为空:" << map1.empty() << endl;
return 0;
}
2.4 迭代器 iterator
容器的遍历通常都是使用迭代器进行的,C++标准库为每一种容器都提供了对应的迭代器类型。
迭代器如同一个指针,但是迭代器又不仅仅是指针。
每种容器都定义了特殊的函数,用于返回所需类型的迭代器,通常情况下使用begin函数获得顺序迭代器类型,表示只读。
还有const_interator类型,表示只读。
#include <iostream>
#include <string>
#include <array>
#include <vector>
#include <list>
#include <deque>
#include <map>
using namespace std;
int main (){
//string容器
cout << "string容器:";
string str = "ABCDEFG";
for(string::const_iterator iter = str.begin();iter != str.end();iter ++){
cout << *iter << " ";
}
cout << endl;
//array容器
cout << "array容器:";
array<string,7> arr = {"Any","Boy","Cat","Dog","Each","Far","God"};
for(array<string,7>::const_iterator iter = arr.begin();iter != arr.end();iter ++){
cout << *iter << " ";
}
cout << endl;
//vector容器
cout << "vector容器:";
vector<string> vec = {"Any","Boy","Cat","Dog","Each","Far","God"}; //使用初始化列表
for(vector<string>::const_iterator iter = vec.begin();iter != vec.end();iter ++){
cout << *iter << " ";
}
cout << endl;
//deque容器
cout << "deque容器:";
deque<string> deq = {"Any","Boy","Cat","Dog","Each","Far","God"};
{
deque<string>::const_iterator iter = deq.begin();
while(iter != deq.end()){
cout << *iter << " ";
iter ++;
}
cout << endl;
}
//list容器
cout << "list容器:";
list<string> lis = {"Any","Boy","Cat","Dog","Each","Far","God"};
{
list<string>::const_iterator iter = lis.begin();
while(iter != lis.end()){
cout << *iter << " ";
iter ++;
}
cout << endl;
}
//关联容器
cout << "关联容器:" << endl;
map<string,int> map1 = {{"身高",178},{"体重",121},{"年龄",22},{"薪资",18000}};
for(map<string,int>::const_iterator iter = map1.begin();iter != map1.end();iter ++){
cout << iter->first << ":" << iter->second << endl;
}
return 0;
}