模板、STL标准模板库

news2025/1/10 21:11:46

模板

通常 对 具有相同要求的结果或者类 提供一个模板,根据实际使用时传过来的数据类型,决定函数和类的具体实现。
模板可以让类或者函数支持一种类型,这种通用类型在实际运行的过程中可以使用任何数据类型。
这种编程方式也成为"泛型编程"。

模板函数

如果函数除了参数类型和返回值类型以外,其他部分全部相同,就可以使用模板来定义函数。

template <typename T> // T:数据类型
template <typename T1, typename T2, …>

#include <iostream>
using namespace std;

template <typename T>		// 模板只对下面的第一个函数有效
T func()					// 参数没有使用模板中的类型,或者无参,不能通过函数调用直接推导出 T 的类型
{							// 需要显性调用模板
    return 38;
}

template <typename T>		// 模板只对下面的第一个函数有效
T func(T a, T b)			// 两个参数都是模板的数据类型,可以通过函数调用推导出模板类型
{							// 隐性调用模板
    return a > b ? a : b;
}

template <typename T1, typename T2>			// 模板只对下面的第一个函数有效
T2 add(T1 a, T1 b)
{	
    T2 num;					// 模板提供的数据类型也可以在函数里面定义变量使用
    return a + b;
}

int main()
{
    int num1 = 9;
    int num2 = 6;

    cout << func<char>() << endl;
    cout << func(num1, num2) << endl;
    cout << func(6, 9) << endl;			// 若用小数,必须每个值都用小数,∵T func(T a, T b)
    cout << add<int, int>(num1, num2) << endl;
    cout << add<float, float>(6, 9) << endl;

    return 0;
}

在这里插入图片描述

模板类

如果要 给模板类的成员函数 实现 类内声明类外定义,需要在类外定义的位置再重写一次模板,并且,类要使用显性调用模板来实现。

#include <iostream>
using namespace std;

// 实现模板类
template <typename T>			// 可以使用 class,也可以使用 typename
class Complex
{
    T real;
    T vir;
public:
    Complex() { }
    Complex(T real, T vir):real(real), vir(vir) { }
    void set_(T real, T vir);  // 模板类中的成员函数,可以实现类内声明,类外定义,需要重写模板
    void show();			   // 模板类中的成员函数也可以在类内定义
};

template <typename T>  		// 只对下面一个模板函数有效
void Complex<T>::set_(T real, T vir)
{
    this->real = real;
    this->vir = vir;
}

template <typename T>		// 只对下面一个模板函数有效
void Complex<T>::show()
{
    cout << real << "+" << vir << "i" << endl;
}

int main()
{
    Complex<int> com(3, 4);
    com.show();
    com.set_(5, 12);
    com.show();
    
    return 0;
}

在这里插入图片描述

STL 标准模板库(Standard Template Library)

https://en.cppreference.com/w/(👈,放心跳转)
在这里插入图片描述

标准模板库(Standard Template Library,STL)是惠普实验室开发的一系列软件的统称。虽说它主要出现到了 C++ 中,但是在被引入 C++ 之前该技术就已经存在了很长时间。
STL 的代码从广义上讲分为三类:algorithm(算法)、container(容器)和 iterator(迭代器),几乎所有的代码都采用了模板类和模板函数的方式,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。

C++ Iterators(迭代器)

迭代器是一个特殊的指针,主要用于元素的读写以及遍历。
在这里插入图片描述

找指定位置的迭代器

由于容器类只能找起始位置和结束位置的迭代器,所以只能在已有迭代器的位置上自增,和指针类似。访问元素需要解引用,但是不能像指针类型一样强转。

容器名<数据类型> ::iterator 迭代器名;

迭代器遍历

如果 迭代器不进行修改操作,建议使用只读迭代器 const_iterator,反之使用 iterator。
#include <iostream>
#include <array>
#include <vector>
#include <list>
#include <deque>
#include <map>

using namespace std;


int main()
{
    // string
    string s = "abcdefg";
    for(string::const_iterator iter = s.begin();
        iter != s.end(); iter++)
    {
        cout << *iter;
    }

    cout << endl;
    cout << "-----------" << endl;

    // array
    array<int, 5> arr = {21, 2, 4, 67, 3};
    for(array<int, 5>::const_iterator iter = arr.begin();
        iter != arr.end(); iter++)
    {
        cout << *iter << " ";
    }

    cout << endl;
    cout << "-----------" << endl;
    
    // vector
    vector<string> vec(6, "world");
    for(vector<string>::const_iterator iter = vec.begin();
        iter != vec.end(); iter++)
    {
        cout << *iter << " ";
    }

    cout << endl;
    cout << "-----------" << endl;
    
    // list
    list<string> lis(5, "hello");
    for(list<string>::const_iterator iter = lis.begin();
        iter != lis.end(); ++iter) 		// 等同于 iter++
    {
        cout << *iter << " ";
    }

    cout << endl;
    cout << "-----------" << endl;

    // deque
    deque<string> de(6, "Hola~");
    for(deque<string>::const_iterator it = de.begin();
        it != de.end(); it++)
    {
        cout << *it << " ";
    }

    cout << endl;
    cout << "-----------" << endl;
    
    // map
    map<string, int> ma;
    ma["waistline"] = 66;
    ma["type"] = 1;
    ma["height"] = 188;
    ma["asset"] = 202300;

    for(map<string, int>::const_iterator i = ma.begin();
        i != ma.end(); i++)
    {
        // first 是键(key),second 值(val)
        cout <<  i->first << " " << i->second << endl;
    }
    return 0;
}

在这里插入图片描述

容器(= 顺序容器 + 关联容器)

用来存储数据的集合,数据元素可以是任何类型(因为是使用模板进行实现的)。
容器类的使用,需要引入对应的头文件。
在这里插入图片描述

顺序容器

顺序容器中每个元素均有固定的位置并呈现线性排布,
除非使用删除或者插入的操作改变原来元素的位置。

Array 数组

array 数组是 C++11 新增的容器类型,与传统数组相比更加安全,易于使用。array 数组是定长的。

EXAMPLE
#include <iostream>
#include <string.h>
#include <array>  						// 头文件

using namespace std;

int main()
{
    // 创建一个长度为 5 的 int 数组
    array<int, 5> arr = {1, 2, 3}; 		// 后面两位补零
    cout << arr[0] << endl; 			// 1
    cout << arr[4] << endl; 			// 0
 
    cout << arr.at(2) << endl; 			// 3,推荐使用 at函数(安全)

    arr[3] = 200;

    cout << "------------" << endl;
    // for 循环遍历
    for(int i = 0; i < arr.size(); i++)
    {
        cout << arr.at(i) << endl;
    }

    cout << "------------" << endl;
    // for each 遍历
    for(int i : arr)
    {
        cout << i << endl;
    }
    
    return 0;
}

在这里插入图片描述

Vector 向量

vector 的行为和数组类似,可以理解为顺序表。
vector 内部是由数组实现的,比较适合进行随机的存取操作,不擅长插入和删除操作
vector 不需要判满,动态分配内存:如果存入新的数据,会再开辟一片更大的空间,把原来的内容拷贝过去。
在这里插入图片描述

构造函数

在这里插入图片描述

Functions
bool empty();

在这里插入图片描述

size_type size();

在这里插入图片描述

TYPE at (size_type loc);

在这里插入图片描述

iterator begin();

在这里插入图片描述

iterator end();

在这里插入图片描述

void push_back (const TYPE &val);

在这里插入图片描述

size_type capacity();

在这里插入图片描述

void pop_back();

在这里插入图片描述

TYPE front();

在这里插入图片描述

TYPE back();

在这里插入图片描述

insert 函数

iterator insert (iterator loc, const TYPE &val);
void insert (iterator loc, size_type num, const TYPE &val);
void insert (iterator loc, input_iterator start, input_iterator end);

在这里插入图片描述

assign 函数

void assign (input_iterator start, input_iterator end);
void assign (size_type num, const TYPE &val);
在这里插入图片描述

EXAMPLE
#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<int> v1;   		// 调用 vector 的无参构造
    vector<int> v2(5, 3);
    vector<int> v3(v2);   	// 调用拷贝构造
    cout << v1.empty() << endl;		// 判断 v1 是否为空
    cout << "元素个数:" << v2.size() << endl;   	// 输出 v2 容器中元素的个数
    cout << "v2的大小:" << v2.capacity() << endl;  	// 5
    
    v2.push_back(89);				// 尾插
    cout << "元素个数:" << v2.size() << endl;
    cout << "v2的大小:" << v2.capacity() << endl;  // 插入一个元素后,是 10,二倍扩容
    cout << "V2中的元素:" << endl;
    v2.push_back(89);
    cout << "\t push 后元素个数:" << v2.size() << endl;
    v2.pop_back();
    cout << "\t pop 后元素个数:" << v2.size() << endl;
    cout << "v2的大小:" << v2.capacity() << endl;
    
    v2.front() = 45;
    v2.back() = 78;

    // 在第三个位置前插入元素,需要用到 insert 函数
    vector<int>::iterator pos = v2.begin()+2;
    v2.insert(pos, 29);
    vector<int>::iterator temp;   // 定义一个可以遍历 <int> 模板的 vector 容器的迭代器
    for (temp = v2.begin(); temp != v2.end(); temp++)
        cout << *temp << "\t";  //对迭代器解引用操作,访问到具体的元素
    cout << endl;
    
    cout << *(v2.end()-1) << endl;
    
    /*cout << v2.front() << endl;  		// 返回对象的引用
    cout << v2.back() << endl;*/
    
    cout << v2.size() << endl;
    pos = v2.begin()+4;  		// 和指针的操作相同,从第一个迭代器找下一个迭代器直接 +1
    temp = v2.end();
    v2.assign(pos, temp);
    for (temp = v2.begin(); temp != v2.end(); temp++)
    {
        cout << *temp << "\t";  //对迭代器解引用操作,访问到具体的元素
    }
    cout << endl;

    return 0;
}

在这里插入图片描述

( begin / end ) v.s. ( front / back )

begin 和 end 成员函数,返回起始位置和结尾位置的迭代器;
front 和 back 成员函数,返回起始位置和结尾位置的引用。

vector 的二倍扩容

vector<int> v2(5, 3); // 第一次开辟 5 个 int 型 大小(5个3)
v2.push_back(89); // v2 容器中元素满,再插入 89,则再开辟 5 个 int 型 大小
// 若再满,再插入,则再开辟 10 个 int 型 大小。然后是 20,40…以此类推。

List 双向链表

list 内部有双向循环链表的实现,内存空间不连续,不支持下标。
可以进行高效的删除和添加操作,但是不适合随机存取。

#include <iostream>
#include <list> 				// 头文件
using namespace std;

int main()
{
    // 创建一个默认无数值的 list
    // list<string> lis1;

    // 创建一个长度为 2 的列表,第一个元素 "hello",第二个元素 "world"
//    list<string> lis2{"hello", "world"};
//    for(string s : lis2)
//    {
//        cout << s << endl;
//    }

    // 创建一个长度为 5 的列表,每个元素都是 "hello"
    list<string> lis(5, "hello");

    // 增
    lis.push_back("world"); 			// 向后追加单元素
    lis.push_front("hahaha"); 			// 向前追加单元素
    lis.insert(++lis.begin(), "222"); 	// 在第二个位置上插入"222"

    for (list<string>::iterator iter = lis.begin(); iter != lis.end(); iter++)
        cout << *iter << ", ";
    cout << endl << "----------------" << endl;

    // 删
    lis.pop_back(); 			// 删除最后一个元素
    lis.pop_front(); 			// 删除第一个元素


    // 迭代器
    list<string>::iterator iter = lis.begin();
    advance(iter, 1); 			// 移动迭代器指针到固定位置
    lis.insert(iter, "333");

    lis.push_back("world"); // 向后追加单元素
    // 删除最后一个元素
    iter = lis.end();
    iter--;
    lis.erase(iter);

    // 删除
    iter = lis.begin();
    advance(iter, 1);
    lis.erase(iter);

    // 返回最后一个元素
    cout << "The last: " << lis.back() << endl;
    // 返回第一个元素
    cout << "The first: " << lis.front() << endl;

    cout << "----------------" << endl;
    // 不能用普通循环遍历,因为不支持下标
    for(string s : lis)
    {
        cout << s << endl;
    }

    cout << "Size = " <<lis.size() << endl;
    lis.clear();
    cout << "Size = " << lis.size() << endl;

    return 0;
}

在这里插入图片描述

Deque 双端队列

队列几乎支持所有 vector 的API,性能位于 vector 与 list 二者之间,是擅长两端存取的顺序容器。

#include <iostream>
#include <deque>			// 头文件
using namespace std;

int main()
{
    
//    deque<int> v(5);
//    for(int i : v)
//    {
//        cout << i << endl;
//    }
    
    // 创建一个长度为 5 的 int 向量
    deque<int> vec = {1, 2, 3, 4, 5};

    // 增
    // 向后追加一个元素
    vec.push_back(222);
    // cout << vec.size() << endl;
    // begin()可以返回指向第一个元素的迭代器指针,+2是在第三个位置上插入333
    vec.insert(vec.begin()+2, 333); // 1 2 333 3 4 5 222

    // 删
    // 删除最后一个元素
    vec.pop_back(); 				// 1 2 333 3 4 5

    // 删除第二个元素
    vec.erase(vec.begin() + 1);		// 1 333 3 4 5

    // 删除倒数第二个元素
    vec.erase(vec.end() - 2); 		// 1 333 3 5

    // 改
    vec.at(2) = 666; 				// 1 333 666 5
    vec[1] = 222;   				// 1 222 666 5

    // 查
    cout << vec.at(1) << endl;
    cout << vec[0] << endl;

    // 遍历
    for(int i :vec)
    {
        cout << i << " ";
    }

    cout << endl;
    cout << "-----------" << endl;

    for(int i = 0; i < vec.size(); i++)
    {
        cout << vec[i] << " " ;
    }
    
    cout << endl;
    cout << "-----------" << endl;
    
    // 判断是否为空,0:非空 1:空
    cout << vec.empty() << endl;

    // 清空
    vec.clear();
    cout << vec.empty() << endl;
    
    return 0;
}

在这里插入图片描述

关联容器

关联容器的各元素之间没有严格的顺序,虽然内部具有排序特点,但在使用时没有任何顺序相关接口。
最常见的关联容器就是 map —— 键值对映射。

Map

对于 map 而言,键具有唯一性,键通常使用字符串类型,
值可以是任何类型,通过键可以找到对应的值。
在这里插入图片描述

#include <iostream>
#include <map> 					// 头文件
using namespace std;

int main()
{
    // 列表初始化创建 c++11 支持
//    map<string, int> ma1 = {{"年龄", 5}, {"身高", 200}};
//    cout << ma1.size() << endl; 					// 2
    
    // 创建一个元素为 0 的键值对对象
    map<string, int> ma;
    cout << ma.size() << endl;
    ma["height"] = 185; 							// 插入元素
    ma.insert(pair<string, int>("weight", 140)); 		// 插入元素

    // 查
    cout << "Height: " << ma["height"] << endl;
    cout << "Weight: " << ma["weight"] << endl;
    
    // 改
    ma["height"] = 188;
    cout << "Height: " << ma["height"] << endl;

    // 删
    if(ma.find("weight") == ma.end())
    {
        cout << "Cannot find the key named \"weight\"! "<< endl;
    }
    else
    {
       int re = ma.erase("weight");
       cout << "Succeeded or not: " << re << endl;
    }

    cout << ma.size() << endl;
    
    ma.clear();
    cout << ma.size() << endl;
    
    return 0;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1282310.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

uniapp自定义进度条组件

目标效果 原型设计为这样的样式&#xff0c;但是现有的进度条都无法满足需求&#xff0c;于是编写组件实现。 设计引用格式为 <zLineProgress :total"15" :val"7" title"你好吗" />定义组件 <template><view style"hei…

TikTok动态展示广告是什么?

TikTok 的动态展示广告 (DSA) 是一种定制视频广告&#xff0c;它们是根据广告模板实时创建的&#xff0c;并填充定期更新的产品目录中的产品信息。DSA 是 TikTok 版本的动态产品广告&#xff0c;是社交广告中受到卖家欢迎的一种形式&#xff0c;主要是在应用程序和社交广告平台…

【java+vue+微信小程序项目】从零开始搭建——健身房管理平台(1)spring boot项目搭建、vue项目搭建、微信小程序项目搭建

项目笔记为项目总结笔记,若有错误欢迎指出哟~ 【项目专栏】 【java+vue+微信小程序项目】从零开始搭建——健身房管理平台(1)项目搭建 持续更新中… java+vue+微信小程序项目】从零开始搭建——健身房管理平台 项目简介Java项目搭建(IDEA)1.新建项目2.项目类型3.项目设置4…

❀My学习Linux命令小记录(10)❀

目录 ❀My学习Linux命令小记录&#xff08;10&#xff09;❀ 36.fold指令 37.expr指令 38.iperf指令 39.telnet指令 40.ssh指令 ❀My学习Linux命令小记录&#xff08;10&#xff09;❀ 36.fold指令 功能说明&#xff1a;控制文件内容输出时所占用的屏幕宽度&#xff0c…

Android12之MediaCodec硬编解码调试手段(四十九)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只…

YOLOv8创新魔改教程(二)如何添加注意力机制

YOLOv8创新魔改教程&#xff08;二&#xff09;如何添加注意力机制 &#xff08;一&#xff09;找代码 github找各种注意力机制的代码 &#xff08;二&#xff09;融合 1.创建文件 在ultralytics/nn/attention.py创建attention.py 文件 将找到的代码粘贴进来 2.修改task…

MySQL find_in_set函数的深入解析与应用

theme: smartblue 在数据库操作中&#xff0c;我们经常会遇到需要处理以逗号分隔的字符串&#xff0c;并且需要根据这些字符串进行查询的情况。MySQL提供了一个非常实用的函数FIND_IN_SET()来处理这种特定的查询需求。本文将深入解析FIND_IN_SET()函数的使用方法&#xff0c;并…

Disucz论坛必备哪些插件,最全Disucz插件【2023最新】

社区论坛的重要性愈发凸显。Disucz作为一款开源的社区论坛软件&#xff0c;其灵活性和可扩展性使其成为许多网站社区的首选。要充分发挥Disucz的潜力&#xff0c;选择并安装适当的插件是至关重要的。 Disucz插件大全 Disucz插件的多样性和功能丰富性为用户提供了个性化和高度…

Centos系列:Centos7下部署nginx(三种方式安装部署,图文结合超详细,适合初学者)

Centos7下部署nginx&#xff08;三种方式安装部署&#xff0c;图文结合超详细&#xff0c;适合初学者&#xff09; Centos7下部署nginx一. ngxin是什么二. nginx的作用正向代理和反向代理的区别 三. 安装部署安装环境1. yum安装配置nginx源启动nginx浏览器访问&#xff0c; IP:…

详细了解 MOSFET 晶体管

MOSFET 开关晶体管 MOS 管是 “金属&#xff08;Metal&#xff09;氧化物&#xff08;Oxide&#xff09;半导体&#xff08;Semi&#xff09;” 场效应晶体管&#xff0c;或者称是 “金属&#xff08;Metal&#xff09;绝缘体&#xff08;Insulator&#xff09;半导体&#xf…

微信公众号端在线客服系统源码 聊天记录云端实时保存 附带完整的搭建教程

随着社交媒体的普及&#xff0c;越来越多的用户通过微信公众号与企业进行沟通。因此&#xff0c;开发一款基于微信公众号的在线客服系统&#xff0c;可以帮助企业更好地服务用户&#xff0c;提高客户满意度。同时&#xff0c;为了解决聊天记录的存储和管理问题&#xff0c;我们…

iOS17苹果备忘录怎么设置提醒?

在我们快节奏的生活中&#xff0c;苹果备忘录成了记录灵感、任务和重要事项的得力助手&#xff0c;面对着一个让人头疼的问题——备忘录竟然不能设置提醒&#xff01;突然感觉我的备忘录只是个寂寞的清单&#xff0c;没有提醒的陪伴。 于是&#xff0c;我着手寻找解决之道&…

02_W5500网络初始化

如何与W5500通信&#xff1f; 我们在W5500介绍中可以看到W5500支持SPI通信协议&#xff0c;如果对SPI通信协议还不太了解&#xff0c;请转 SPI数据帧&#xff1a; W5500 的 SPI 数据帧包括了 16 位地址段的偏移地址&#xff0c; 8 位控制段和 N 字节数据段。 如图所示…

行业分析:2023年藜麦市场竞争格局及发展现状分析

藜麦是藜科藜属植物。穗部可呈红、紫、黄&#xff0c;植株形状类似灰灰菜&#xff0c;成熟后穗部类似高粱穗。植株大小受环境及遗传因素影响较大&#xff0c;从0.3-3米不等&#xff0c;茎部质地较硬&#xff0c;可分枝可不分。单叶互生&#xff0c;叶片呈鸭掌状&#xff0c;叶缘…

科研小白必收藏,手把手教你写医学论文!

一篇完整的论文应包括标题、摘要、引言、方法、结果、讨论、致谢、参考文献八个部分&#xff0c;每个部分的写作都有技巧。 1.标题 标题一般采用名词词组或名词短语的形式&#xff0c;个别杂志也允许陈述句的形式。标题必须简单、明了&#xff0c;醒目。题目要新颖&#xff0c…

向库存抢利润!DigiOS微服务“库存中心”能力解读

作者&#xff1a;徐礼昭&#xff08;商派市场负责人&#xff0c;重构零售实验室负责人&#xff09; 同一件SKU&#xff0c;在不同渠道往往会出现“超卖”和“滞销”两种截然不同的情况。如何及时合理的调拨库存&#xff0c;实现产品的最大化销售&#xff08;降低库存成本&#…

Linux系统中进程间通信(Inter-Process Communication, IPC)

文章目录 进程间通信介绍进程间通信目的进程间通信发展 管道什么是管道 匿名管道用fork来共享管道原理站在文件描述符角度-深度理解管道站在内核角度-管道本质管道读写规则管道特点 命名管道创建一个命名管道匿名管道与命名管道的区别命名管道的打开规则 命名管道的删除用命名管…

Java Web——动态Web开发核心-Servlet

目录 1. 官方文档 2. Servlet概述 3. Servlet基本使用 3.1. 浏览器调用Servlet的流程 3.2. Servlet生命周期 3.3. Get/Post请求的分发处理 4. Servlet继承结构 5. 继承HttpServlet开发Servlet 5.1. HttpServlet介绍 5.2. 使用相关代码测试 5.3. HttpServletRequest …

路由策略,gRPC 路由如何实现

目录 一、为啥我们要路由策略&#xff1a; 二、基于gRPC 路由策略 一、为啥我们要路由策略&#xff1a; 我们可以重新回到调用方发起 RPC 调用的流程。在 RPC 发起真实请求的时候&#xff0c;有一个步骤就是从服务提供方节点集合里面选择一个合适的节点&#xff08;就是我们…

编程应用实例,早点快餐店点餐软件支持零售价和会员价,软件定制开发

编程应用实例&#xff0c;早点快餐店点餐软件支持零售价和会员价&#xff0c;软件定制开发 一、编程应用实例&#xff1a; 软件适用范围&#xff1a; 1、早点 2、快餐店 3、面馆 4、汉堡店 5、奶茶店 6、饭店等 程序说明&#xff1a; 二、程序说明&#xff1a; 1、软件…