c++入门学习⑧——模板

news2025/1/20 3:39:58

目录

前言

基本介绍

什么是模板?

作用

特点

分类

函数模板

语法

使用方式

注意事项

函数模板和普通函数区别

普通函数和函数模板的调用规则

局限性

类模板

语法

类模板的成员函数创建时机

类模板实例化对象

类模板实例化对象做函数参数

类模板成员类外实现

类模板分文件编写

类模板和继承

类模板和友元

总结


前言

 该系列的上篇文章介绍了有关c++继承和多态的详细知识,那么本篇文章就接着介绍有关c++的模板知识,希望对大家有所帮助(●'◡'●)

编程思想除了面向对象(封装、继承、多态)以外还有泛型编程——主要通过模板来实现

基本介绍

什么是模板?

在生活中其实模板这个词很常见——假如你要写一份简历,这时候需要准备一份模板,然后按照自己的情况来填写内容。

而在编程中其实也是差不多的概念:

具体来说——模板具体的概念如下:

模板实际上是先建立一个通用函数或者类,其类内部的类型和函数的形参类型不具体指定,用一个虚拟的类型来代表,这种通用的方式称为模板。

而模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码

作用

模板有什么作用?

实际上学习模板并不是为了写模板,而是在STL能够运用系统提供的模板(有关stl的知识见下篇文章( ̄▽ ̄))

特点

那模板有什么特点呢?

  • 模板不可以直接使用,它只是一个框架
  • 模板的通用并不是万能的,它有一定的局限

它的语法是怎样写的呢?

无论哪一种形式,都需要一个关键字——temple

语法通常由于类型的不同而写的形式有所出入,那么这里我们先介绍一下模板的分类再来详细介绍模板的语法

分类

分为函数模板和类模板

函数模板

无论在哪,都要先写一句:template (class/typename T)

这句话是为了让C++编译器知道要开始泛型编程

语法

关于函数模板,这里通过一个例子引入:

#include<iostream>
#include<string>
using namespace std;
template<class T>
void Swap(T &a,T &b)
{
    T temp = b;
    b = a;
    a = temp;
}
void test1()
{
    int a = 1;
    int b = 2;
    Swap(a, b);
    cout << "a:" << a << endl;
    cout << "b:" << b << endl;
}
int main()
{
    test1();
    system("pause");
    return 0;
}

这里构建了一个函数模板——用于交换两个数的值

由这个代码,我们可以了解函数模板的语法以及用法:

先写这一行代码:

template<class T>或者是template<typename T>(注:这里的T可以换成其他的)

而下面紧接着的函数——就是所谓的模板函数,而在模板函数中传入参数是刚才定义的T类型
void Swap(T &a,T &b)
{
    T temp = b;
    a = temp;
    b = a;
}

而在使用时,可以直接传入两个int类型的,在程序运行时,函数也会正常调用

使用方式

函数模板使用有两个方法:

  • 自动类型推导
  • 显示指定类型

第一种类型就是直接传入数据,函数模板会自己推导

第二种类型就是调用函数时,直接在函数名之后加上<指定类型>

注意事项

当然刚才所介绍的语法以及调用并不是函数模板使用时的全部,它还有一些事项需要注意:

  1. 自动类型推导,必须推导出一致的数据类型T才可以使用
  2. 模板必须要确定出T的数据类型,才可以使用

下面来用代码来分别讲解一下

1.自动类型推导,必须推导出一致的数据类型T才可以使用

当我在刚才的代码中加入一个double类型的数据,然后把它传入Swap函数,

结果它显示出错了——出错原因是这个函数模板自动推导出来的类型是int和double,但是T只能代表一个数据类型,这时候如果传入不同的数据类型,则会出错

2.模板必须要确定出T的数据类型,才可以使用

这个条件很容易解释,如果一个函数的参数无法确定参数,那么如何对其进行运行呢?

函数模板和普通函数区别

  1. 普通函数调用可以发生隐式类型转换
  2. 函数模板用自动类型推导,不可以发生隐式类型转换
  3. 函数模板用显示指定类型,可以发生隐式类型转换

我们还是同样的一条一条解释:

1.普通函数调用可以发生隐式类型转换

注意:这个类型转换不是传入的所有的都会转化为对应数据类型的参数,要看情况。

代码示例:

#include<iostream>
using namespace std;
//普通函数调用可以发生隐式类型转换

int add(int a, int b)
{
    return a + b;
}
void test1()
{
    int a = 1;
    int b = 2;
    char c = 'c';
    cout << "a+b=" << add(a,b) << endl;//3
    cout << "a+c=" << add(a,c) << endl;//100
}
int main()
{
    test1();
    system("pause");
    return 0;
}

2.函数模板用自动类型推导,不可以发生隐式类型转换

当我们在上面的函数上添加一行代码template<class T>然后把所有int改为T,

就会发现,程序开始报错:

3.函数模板用显示指定类型,可以发生隐式类型转换

那函数模板可不可以发生隐形类型转换呢?——答案是可以

当我们使用显示指定类型,在函数调用时在函数名后面加上指定类型,

程序正常运行。

普通函数和函数模板的调用规则

函数模板和普通模板的调用规则一样吗?

当普通函数和函数模板都可以实现时,调用谁呢?如果调用其中一个,那么另一个该怎么去调用呢?可以强制调用另一个吗?

我们先写个代码来看一下:

#include<iostream>
#include<string>
using namespace std;

template<class T>
T add(T a, T b)
{
    return a + b;
}
int add(int a, int b)
{
    return a + b + b;
}
void test1()
{
    int a = 1;
    int b = 2;
    cout << "a+b=" << add(a,b) << endl;//5
}
int main()
{
    test1();
    system("pause");
    return 0;
}

当函数模板和普通函数同时存在,而且传入参数都可以实现时,输出时,发现是——调用的是普通函数产生的结果。

由此产生第一条规则:

如果函数模板和普通函数都可以实现,优先调用普通函数

那如何在这种情况下调用函数模板呢?

——添加一个空模板参数列表<>


第二条规则:

可以通过空模板参数列表来强制调用函数模板

当调用时,函数模板是较于普通函数更好的匹配,则优先调用函数模板:

例如:

第三条规则是:

如果函数模板可以产生更好的匹配,优先调用函数模板

第四条规则是:

函数模板也可以发生重载

总结:

1如果函数模板和普通函数都可以实现,优先调用普通函数
2.可以通过空模板参数列表来强制调用函数模板
3.函数模板也可以发生重载
4.如果函数模板可以产生更好的匹配,优先调用函数模板

⭐注意:实际开发中不会同时出现这两个函数——避免二义性

局限性

当自定义数据类型使用模板函数时,会出错,这是因为函数模板有一定的局限性,它无法去判断自定义数据类型如何通过模板工作,因此遇到这种情况,我们该如何解决这个局限性呢?

这里有两种方法:

1.运算符重载,把运算符重载后,函数模板就可以让自定义数据类型正常使用了,因为系统直到了如何应对这个自定义数据了

2.具体化这个函数模板:template<> 返回值  函数名(自定义数据类型   函数参数){函数体}

举例说明:

bool xd(person &p1, person &p2)
{
if (pl.m namep2.m name &&plmmagep2.m_age)
{
return true;
}
else
{
return false;
}
}

 一般情况来说,使用第二种较为方便

类模板

介绍完函数模板,下面来介绍类模板。

注意:

类模板没有自动类型推导使用方式
类模板在模板参数列表可以有默认参数

语法

还是先写template <class/typedef  T>

后面紧跟着类——为类模板

示例:

template<class A,class B>
class people
{
public:
    A m_age;
    B m_name;
};

类模板的成员函数创建时机

⭐对于类模板来说,它的成员函数是与一般类相比有所区别

——创建时机不一样
普通类中的成员函数——开始时就可以创建
类模板中的成员函数——在调用时才创建

类模板实例化对象

#include<iostream>
#include<string>
using namespace std;

template<class T,class A>
class people
{
public:
    people(T age, A name)
    {
        this->m_age = age;
        this->m_name = name;
    }
    T m_age;
    A m_name;
};

void test1()
{
    people<int,string> a(12, "小红");
}
int main()
{
    test1();
    system("pause");
    return 0;
}

这么长的一段代码——其实重点就只有几行——

 

类名 <参数列表>  类对象名称(初始化内容);

类模板实例化对象做函数参数

当类模板对象作为函数参数时,如何传入,其注意事项有哪些?

这里先介绍传入方式——有三种
1.指定传入的类型⭐(一般用这个)
2.参数模板化 ---将对象中的参数变为模板进行传递
3.整个类模板化 ---将这个对象类型模板化进行传递

下面来用一段代码来解释一下

当创建一个输出函数,然后把这个对象传入时,发生了错误

而第一种方式是:加上参数列表

方法2:把这个参数变为模板传递

方法3:把这个传入的类模板化

完整代码:

#include<iostream>
#include<string>
using namespace std;

template<class T,class A>
class people
{
public:
    people(T age, A name)
    {
        this->m_age = age;
        this->m_name = name;
    }
    T m_age;
    A m_name;
};
//1.指定传入的类型
void out1(people<int,string> & a)
{
    cout << a.m_name << endl;
    cout << a.m_age  << endl;
}
//2.参数模板化-- - 将对象中的参数变为模板进行传递
template<class A1,class B1>
void out2(people<A1,B1> & a)
{
    cout << a.m_name << endl;
    cout << a.m_age << endl;
}

//3.整个类模板化-- - 将这个对象类型模板化进行传递
template<class T>
void out3(T& a)
{
    cout << a.m_name << endl;
    cout << a.m_age << endl;
}
void test1()
{
    people<int,string> a(12, "小红");
    out1(a);
    out2(a);
    out3(a);
}
int main()
{
    test1();
    system("pause");
    return 0;
}

类模板成员类外实现

类模板成员是否可以实现类内声明,类外初始化呢?和普通函数一样,加上作用域就可以了吗?

答案是❌发生错误

这里分为两类函数讲解——构造函数和成员函数

构造函数:

解决方案如下:

普通成员函数:

如上,无论是哪一个,都需要先写template<默认参数列表>

然后函数都需要写上作用域,然后再在类名后加上<默认参数列表>

类模板分文件编写

在实际开发中通常会把声明和实现一个放在头文件,一个放在源文件,如果我们把类模板的实现和声明拿出来,放在people.h和people.cpp中,然后这个原本的代码加上#include“people.h”再次运行,是否可以达到想要的效果?

刚才我们谈到,类模板的创建时机和普通函数并不相同,只有在调用它的时候才会创建,而现在这个代码包含的是头文件——头文件只有声明无实现,编译器根本不知道函数实现的代码,因此错误。

那解决方案呢?

1.把包含的文件改成#include"people.cpp"

2.把people.h和people.cpp合并并命名为people.hpp

类模板和继承

当类模板遇到继承会发生怎样的反应?
1.当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
如果不指定,编译器无法给子类分配内存(因为子类继承了这个父类中的未知变量——T类型,分配内存需要知道需要的内存空间)
2.如果想灵活指定出父类中t的类型,子类也需变为类模板

当要继承的父类是类模板时,子类往往需要添加点代码:

指定父类中默认参数的类型,例如:在后面写下指定的类型

如果想要更加灵活的去定义继承中父类的,需要——把子类也要变成类模板

类模板和友元

当类模板碰上友元,如何让类模板配合友元函数的类内和类外实现

全局函数类内实现——直接在类内声明友元即可
全局函数类外实现——需要提前让编译器知道全局函数的存在

全局函数类内实现:

在函数前加一个friend 就可以访问私有权限的内容

类外实现:

这个需要先写类模板的声明,然后写该函数的实现,接着是类模板的实现

总结

模板是一种泛型编程思想的实现,它分为类模板和函数模板,这两个模板都需要使用一个关键字——template

最后感谢观看完毕,欢迎点赞收藏文章或者专栏,如有错误,还请大佬指出(*^_^*) 

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

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

相关文章

堆/堆排序(C/C++)

本篇文章将会较为全面的介绍堆的概念以及实现堆两个重要算法&#xff1a;向上调整算法和向下调整算法。接着实现了堆排序。 若想查看对应位置&#xff0c;可直接按照以下目录进行查看&#xff1a; 目录 1.堆的概念及结构 2.堆的实现 2.1 堆的向上调整算法 2.2 堆的向下调整算法…

【LNMP】云导航项目部署及环境搭建(复杂)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、项目介绍1.1项目环境架构LNMP1.2项目代码说明 二、项目环境搭建2.1 Nginx安装2.2 php安装2.3 nginx配置和php配置2.3.1 修改nginx文件2.3.2 修改vim /etc/p…

LemonSqueezy

信息收集 # nmap -sn 192.168.1.0/24 -oN live.nmap Starting Nmap 7.94 ( https://nmap.org ) at 2024-02-08 11:22 CST Nmap scan report for 192.168.1.1 Host is up (0.00037s latency). MAC Address: 00:50:56:C0:00:08 (VMware) Nmap scan r…

微信小程序开发(实战案例):本地生活 - 列表页面开发(动态渲染处理)、节流防抖(节流阀应用)

文章目录 本地生活 - 列表页面开发一、将九宫格分类换成navigator组件二、动态设置商品列表页的 title三、动态渲染商品列表页面四、上拉触底加载数据五、添加Loading加载效果六、数据加载节流防抖处理 本地生活 - 列表页面开发 导入我们上次写的 本地生活 - 首页开发的项目 运…

2024年2月16日优雅草蜻蜓API大数据服务中心v1.1.1大更新-UI全新大改版采用最新设计ui·增加心率计算器·退休储蓄计算·贷款还款计算器等数接口

2024年2月16日优雅草蜻蜓API大数据服务中心v1.1.1大更新-UI全新大改版采用最新设计ui增加心率计算器退休储蓄计算贷款还款计算器等数接口 更新日志 前言&#xff1a;本次更新中途跨越了很多个版本&#xff0c;其次本次ui大改版-同步实时发布教程《带9.7k预算的实战项目layuiph…

Git笔记——4

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 一、操作标签 二、推送标签 三、多人协作一 完成准备工作 协作开发 将内容合并进master 四、多人协作二 协作开发 将内容合并进master 五、解决 git branch -a…

FreeRTOS任务创建过程详解

本篇文章及记录我在学习FreeRTOS中关于任务创建的详细过程的了解。希望我的分享能给你带来不一样的收获。 目录 一、任务创建的相关函数 二、任务初始化函数分析 三、任务堆栈初始化函数 四、添加任务到就绪列表 一、任务创建的相关函数 前面学了任务创建可以使用动态方法或…

C#学习(十四)——垃圾回收、析构与IDisposable

一、何为GC 数据是存储在内存中的&#xff0c;而内存又分为Stack栈内存和Heap堆内存 Stack栈内存Heap堆内存速度快、效率高结构复杂类型、大小有限制对象只能保存简单的数据引用数据类型基础数据类型、值类型- 举个例子 var c new Customer{id: 123,name: "Jack"…

自定义神经网络二之模型训练推理

文章目录 前言模型概念模型是什么&#xff1f;模型参数有哪些神经网络参数案例 为什么要生成模型模型的大小什么是大模型 模型的训练和推理模型训练训练概念训练过程训练过程中的一些概念 模型推理推理概念推理过程 总结 前言 自定义神经网络一之Tensor和神经网络 通过上一篇…

[深度学习]yolov9+deepsort+pyqt5实现目标追踪

【YOLOv9DeepSORTPyQt5追踪介绍】 随着人工智能技术的飞速发展&#xff0c;目标追踪在视频监控、自动驾驶等领域的应用日益广泛。其中&#xff0c;YOLOv9作为先进的目标检测算法&#xff0c;结合DeepSORT多目标追踪算法和PyQt5图形界面库&#xff0c;能够为用户提供高效、直观…

深度学习500问——Chapter01:数学基础

文章目录 前言 1.1 向量和矩阵 1.1.1 标量、向量、矩阵、张量之间的联系 1.1.2 张量与矩阵的区别 1.1.3 矩阵和向量相乘结果 1.1.4 向量和矩阵的范数归纳 1.1.5 如何判断一个矩阵为正定 1.2 导数和偏导数 1.2.1 导数偏导计算 1.2.2 导数和偏导数有什么区别 1.3 特征值和特征向量…

文献阅读:Transformers are Multi-State RNNs

文献阅读&#xff1a;Transformers are Multi-State RNNs 1. 内容简介2. 方法介绍 1. 基础回顾 1. RNN2. Transformer 2. Transformer解构 1. MSRNN2. Transformer 3. TOVA 1. 现有转换策略2. TOVA 3. 实验考察 & 结论 1. 实验设计2. 实验结果 1. LM2. 长文本理解3. 文本生…

(十三)【Jmeter】线程(Threads(Users))之tearDown 线程组

简述 操作路径如下: 作用:在正式测试结束后执行清理操作,如关闭连接、释放资源等。配置:设置清理操作的采样器、执行顺序等参数。使用场景:确保在测试结束后应用程序恢复到正常状态,避免资源泄漏或对其他测试的影响。优点:提供清理操作,确保测试环境的整洁和可重复性…

通天星CMSV6 车载视频监控平台信息泄露漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

元数据思想-打破传统的思维方式

本文已收录公众号(面汤放盐)&#xff1a;元数据思想-打破传统的思维方式 本文是一篇讲解元数据案例的技术文章; 同时也谈论如何对传统 CRUD 进行破局的文章。 元数据思想-打破传统的思维方式 打破传统的思维模式&#xff0c; 跳出固有的认知模型&#xff0c;从更高的视角去理…

社区分享|中华保险基于MeterSphere开展接口自动化测试

中华联合保险集团股份有限公司&#xff08;以下简称为“中华保险”&#xff09;始创于1986年&#xff0c;是全国唯一一家以“中华”冠名的国有控股保险公司。截至2022年12月底&#xff0c;中华保险总资产为1006.06亿元&#xff0c;在全国拥有超过2900个营业网点&#xff0c;员工…

Python入门必学:reverse()和reversed()的区别

Python入门必学&#xff1a;reverse()和reversed()的区别 &#x1f4c5;2024年02月25日 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程…

ABAP - Function ALV 05 添加选择框列、全选、取消全选

很多用户不习惯原生GRID的选择模式&#xff0c;所以业务需要用到自定义的选择框来进行数据的操作&#xff0c;显示效果如图所示&#xff0c;增加一条选择列&#xff0c;且配置全选和全选全选的按钮功能&#xff0c;如下图所示。 实现这种功能需要用到Fieldcat的参数控制以及GUI…

[02 git ] 清华大学电子系科协软件部2023暑期培训

本视频为清华大学电子系科协软件部2023年暑期培训内容的录屏&#xff0c;主要培训内容为游戏开发、网站建设中常用的软件工具&#xff0c;为未来一年软件部新部员维护科协网站、开发清华大学人工智能挑战赛&#xff08;THUAI&#xff09;作知识铺垫。本次培训还邀请到两位嘉宾讲…

Apache celeborn 安装及使用教程

1.下载安装包 https://celeborn.apache.org/download/ 测0.4.0时出现https://github.com/apache/incubator-celeborn/issues/835 2.解压 tar -xzvf apache-celeborn-0.3.2-incubating-bin.tgz 3.修改配置文件 cp celeborn-env.sh.template celeborn-env.shcp log4j2.xml.…