C++初阶模板

介绍:

        我们先认识以下C++中的模板。模板是一种编程技术,允许程序员编写与数据类型无关的代码,它是一种泛型编程的方式,可以用于创建可处理多种数据类型的函数或类,也就是说泛型编程就是编写与类型无关的通用代码,是代码复用的一种手段,而模板是泛型编程的基础。其中,模板分为函数模板和类模板。


函数模板:

        函数模板是一种通用的函数定义,可以接受多种类型作为输入,并生成对应的特定类型函数。

格式:

        template<typename T1, typename T2, .... , typename Tn>

        template 返回值类型 函数名(参数列表){}

//定义一个可以实现多种类型交换的函数

template<typename T>
void Swap(T& left, T& right)
{
    T temp = left;
    left = right;
    right = temp;
}

        注意:上面的typename是用来定义模板参数的关键字,这可使用class,我们目前可先认为class与typename无任何区别,但是这里不能使用struct来代替。

函数模板的工作原理:

        这里要说明的是函数模板是一个 “ 蓝图 ”,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。

        在编译器编译阶段,当调用函数模板时,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,其它类型也是如此。

        注意:函数模板并不会为每个可能的类型组合生成函数,后文会对齐深入讲解。

函数模板的实例化:

        函数模板根据传入的实参类型生成特定类型的函数,然后调用这些函数,此过程称为函数模板的实例化。其中,模板参数实例化分为:隐式实例化和显式实例化。

        隐式实例化:让编译器根据实参推演模板参数的实际类型。

template<class T>
T Add(const T& left, const T& right)
{
    return left + right;
}
int main()
{
    //隐式实例化
    Add(1, 2);
    Add(1.2, 1.3);
    return 0;
}

        显式实例化:在函数名后的<>中指定模板参数的实际类型。

template<class T>
T Add(const T& left, const T& right)
{
    return left + right;
}
int main()
{
    int a = 10;
    double b = 20.0;
    //显式实例化
    Add<int>(a, b);
    return 0;
}

        注意:当进行类型转换时,如果编译器没有找到与实参类型匹配的函数模板,它会自动进行隐式实例化,如果还无法确定实例化的类型,就会报错。    

        下面我们观察以下代码的问题:

template<class T>
T Add(const T& left, const T& right)
{
    return left + right;
}
template<typename T1, typename T2>
T1 add(const T1& left, const T2& right)
{
    return left + right;
}
int main()
{
    Add(1, 1.2);//系统报错
    Add(1, (int)1.2);//强制转换,运行正常
    Add<int>(1, 1.2);//显示实例化,运行正常

    add(1, 1.2);//参数匹配,运行正常
    return 0;
}

        以上语句不能通过编译是因为在编译期间,当编译器看到该实例化时,需要推演其实参类型
通过实参1将T推演为int,通过实参1.2将T推演为double类型,但模板参数列表中只有一个T,编译器无法确定此处到底该将T确定为int 或者 double类型,进而报错。

        注意:在模板中,编译器一般不会进行类型转换操作。

        解决以上方法有三种:1. 直接强制转化    2. 使用显式实例化     3. 设置多个模板参数

模板函数与非模板函数:

        一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数,当调用时,如果其他条件都相同,会优先调用非模板函数。如果模板可以产生一个具有更好匹配的函数,那么将选择模板函数。

int Add(int left, int right)
{
    return left + right;
}
template<class T>
T Add(T left, T right)
{
    return left + right;
}
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{
    return left + right;
}
int main()
{
    Add(1, 2); //调用非模板函数
    Add<int>(1, 2); //显示实例化,调用模板函数

    Add(1, 2); //与非函数模板类型完全匹配,调用非函数模板
    Add(1, 2.0); //模板函数可以生成更加匹配的版本,调用函数模板
    return 0;
}

        这里再强调一次,模板函数不允许自动类型转换,但普通函数可以进行自动类型转换。


类模板:

        格式如下:

template<class T1, class T2, ..., class Tn>
class 类模板名
{
    ..........
};

        类模板的实例化:类模板的实例化与函数模板的实例化不同,类模板的实例化需要在类模板名字后跟 “ <> ”,然后将实例化的类型放在 “ <> ” 中即可。

template<class T>

class Stack
{
public:
    Stack(int capacity = 4)
    {
        cout << "Stack(int capacity = 4)" << endl;
        _a = new T[capacity];//这里需要用到T,所以不能直接实例化int*或double*
        _top = 0;
        _capacity = capacity;
    }
    ~Stack()
    {
        cout << "~Stack()" << endl;
        delete[] _a;
        _a = nullptr;
        _top = 0;
        _capacity = 0;
    }
    T* _a;
    int  _top;
    int  _capacity;
};
int main()
{
    //显示实例化
    Stack<int> st1;
    cout << typeid(st1._a).name() << endl;//输出int*,因为 T* _a
    Stack<double> st2;  
    cout << typeid(st2._a).name() << endl;//输出double*,因为 T* _a
    return 0;
}

        注意:类模板名字不是真正的类,而实例化的结果才是真正的类,如下:

Stack<int> st1;  //Stack只是类名,Stack<int>才是类型
Stack<double> st2;  //Stack只是类名,Stack<double>才是类型

        当我们使用类模板对象中的成员时,要使用 “ 类型 ” 进行作用域的访问,不能使用类名。

Stack<int>.~Stack() //调用Stack<int>类型的析构函数

Stack<double>.~Stack() //调用Stack<double>类型的析构函数​​​​​​​

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2023-11-28 LeetCode每日一题(设计前中后队列)

2023-11-28每日一题 一、题目编号 1760.设计前中后队列二、题目链接 点击跳转到题目位置 三、题目描述 请你设计一个队列&#xff0c;支持在前&#xff0c;中&#xff0c;后三个位置的 push 和 pop 操作。 请你完成 FrontMiddleBack 类&#xff1a; FrontMiddleBack() 初…

leetcode算法之字符串

目录 1.最长公共前缀2.最长回文子串3.二进制求和4.字符串相乘 1.最长公共前缀 最长公共前缀 class Solution { public:string longestCommonPrefix(vector<string>& strs) {//法一&#xff1a;两两比较string ret strs[0];for(int i1;i<strs.size();i){ret f…

linux下的工具---yum

一、什么是yum yum是Linux下的软件包管理器 二、什么是软件包管理器 1、在Linux下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译, 得到可执行程序. 2、但是这样太麻烦了, 于是有些人把一些常用的软件提前编译好, 做成软件包(可以理解成windows上的安装程序)放在…

深度学习之基于百度飞桨PaddleOCR图像字符检测识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介主要特点使用步骤 二、功能三、系统四. 总结 一项目简介 # Introduction to PaddleOCR Image Character Detection and Recognition System Based on Baidu…

JSON详细教程

&#x1f60a;JSON详细教程 &#x1f6a9;JSON简介☃️JSON语法规则&#x1f50a;JSON和JavaScript对象的区别 ☃️JSON数据类型字符串&#x1f50a;数字&#x1f50a;布尔值&#x1f50a;数组&#x1f50a;对象&#x1f50a;Null ☃️JSON对象&#x1f50a;访问JSON对象的值&a…

bat脚本执行py文件

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

【Python】基础练习题_组合数据类型_2

dictMenu f’卡布奇洛’:32,‘摩卡’:30,‘抹茶蛋糕’:28,‘布朗尼’:26}&#xff0c; dictMenu 中存放了你的双人下午套餐&#xff08;包括咖啡2份和点心2份)的价格,请编写程序,让Python帮忙计算并输出消费总额。 dictMenu {卡布奇洛: 32, 摩卡: 30, 抹茶蛋糕: 28, 布朗尼: 2…

配置zabbix-proxy主动式

IP地址对应关系如下&#xff1a; zabbix-server122.9.8.21zabbix-proxy122.9.4.102zabbix-agent2116.63.9.109 一、 安装zabbix-server https://blog.csdn.net/qq_50247813/article/details/132131774 二、 安装zabbix-proxy a. 安装zabbix源 rpm -Uvh https://repo.zabbix…

【VROC】看Intel VROC如何给NVMe SSD做RAID

在当今对硬盘性能要求越来越高的环境中&#xff0c;SATA和SAS接口由于自身的限制&#xff0c;其性能很难突破600MiB/s的瓶颈。因此&#xff0c;对于需要更高底层硬件性能的行业&#xff0c;如数据库等&#xff0c;对NVMe盘的需求越来越迫切。然而&#xff0c;NVMe盘直通到CPU&a…

Mysql8.1.0 安装问题-缺少visual studio 2019x64组件

缺少visual studio x64组件的问题 使用Mysql8以上的安装包mysql-8.1.0-winx64.msi进行安装&#xff0c; 提示缺少visual studio 2019 x64可再发行组件 在微软官网下载vc可再发行程序包 Microsoft Visual C 可再发行程序包最新支持的下载 在Visual Studio 2015、2017、2019 和…

【Java安全】Java反射机制-成员变量的赋值与取值

文章目录 前言利用反射机制获取类的成员变量利用反射机制获取类的成员方法总结前言 Java反射(Reflection)是Java非常重要的动态特性,通过使用反射我们不仅可以获取到任何类的成员方法(Methods)、成员变量(Fields)、构造方法(Constructors)等信息,还可以动态创建Java类实例、…

12 网关实战:Spring Cloud Gateway基础理论

为什么需要网关? 传统的单体架构中只有一个服务开放给客户端调用,但是微服务架构中是将一个系统拆分成多个微服务,那么作为客户端如何去调用这些微服务呢?如果没有网关的存在,只能在本地记录每个微服务的调用地址。 无网关的微服务架构往往存在以下问题: 客户端多次请求…