C++(模板与容器)
一、模板
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类型数组,长度为4arr[0] = 11; //可以使用[]取内容arr.at(1) = 22; //和string一样可以使用.at()取内容for(auto &i:arr){ //循环遍历for-eachcout << 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); //初始化所有的元素都是0cout << "数组长度:" <<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;elsemap1.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;
}