C++(模板与容器)

发布于:2025-06-27 ⋅ 阅读:(17) ⋅ 点赞:(0)

一、模板

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;
}


网站公告

今日签到

点亮在社区的每一天
去签到