【C++】模板初阶 -- 详解

一、泛型编程

// 实现一个通用的交换函数:
void Swap(int& left, int& right)
{int temp = left;left = right;right = temp;
}void Swap(double& left, double& right)
{double temp = left;left = right;right = temp;
}void Swap(char& left, char& right)
{char temp = left;left = right;right = temp;
}
使用函数重载虽然可以实现,但是有几个不好的地方
  1. 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数。
  2. 代码的可维护性比较低,一个出错可能所有的重载均出错。
在 C++ 中,存在这样一个模具,通过给这个模具中填充不同材料(类型),来获得不同材料的铸件( 即生成具体类型的代码),这会给我们节省很多时间。
泛型编程 :编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。


二、函数模板

1、函数模板概念

函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。

2、函数模板格式

template<typename T1, typename T2, ......, typename Tn>
返回值类型 函数名(参数列表){}
template<typename T> // T代表一个模板类型(虚拟类型)void Swap(T& left, T& right)
{T temp = left;left = right;right = temp;
}

注意typename用来定义模板参数关键字也可以使用class(切记:不能使用 struct 代替 class)


3、函数模板的原理

函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器。

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

4、函数模板的实例化

不同类型的参数 使用函数模板时,称为 函数模板的实例化 模板参数实例化分为:隐式实例化和显式实例化
  1. 隐式实例化:让编译器根据实参推演模板参数的实际类型
    template<class T>
    T Add(const T& left, const T& right)
    {return left + right;
    }int main()
    {int a1 = 10, a2 = 20;double d1 = 10.0, d2 = 20.0;Add(a1, a2);Add(d1, d2);Add(a1, d1); // 无法通过编译// 1、处理方式:用户自己来强制转化Add(a, (int)d);return 0;
    }

    无法通过编译原因:因为在编译期间,当编译器看到该实例化时,需要推演其实参类型。通过实参 a1 将 T 推演为 int,通过实参 d1 将 T 推演为 double 类型,但模板参数列表中只有一个 T,编译器无法确定此处到底该将 T 确定为 int 或者 double 类型而报错。

    注意:在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要背黑锅。

  2. 显式实例化:在函数名后的 <> 中指定模板参数的实际类型
    int main()
    {int a = 10;double b = 20.0;// 2、处理方式:显式实例化Add<int>(a, b);return 0;
    }// 如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错。

5、模板参数的匹配原则

  1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数。
    // 专门处理int的加法函数
    int Add(int left, int right)
    {return left + right;
    }// 通用加法函数
    template<class T>
    T Add(T left, T right)
    {return left + right;
    }int Test()
    {cout << Add(1, 2) << endl; // 与非模板函数匹配,编译器不需要特化cout << Add<int>(1.1, 2.2) << endl;; // 调用编译器特化的Add版本return 0;
    }
  2. 对于非模板函数同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板。
    // 专门处理int的加法函数
    int Add(int left, int right)
    {return left + right;
    }// 通用加法函数
    template<class T1, class T2>
    T1 Add(T1 left, T2 right)
    {return left + right;
    }int Test()
    {Add(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函数return 0;
    }
  3. 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换。

三、类模板

1、类模板的定义格式

template<class T1, class T2, ..., class Tn>
class 类模板名
{// 类内成员定义
};
typedef int STDataType;
class Stack
{
private:STDataType* _a;int _top;int _capacity;
};int main()
{Stack st1; //intStack st2; //doublereturn 0;
}

这里想让 st1 是 int,st2 是 double,显然无法做到。C 语言中的 typedef 只是增强程序的可维护性,不能解决泛型的问题。

template<class T>
class Vector
{ 
public :Stack(size_t capacity = 0){if (capacity > 0){_a = new T[capacity];_capacity = capacity;_top = 0;}}~Stack(){delete[] _a;_a = nullptr;_capacity = _top = 0;}void Push(const T& x); // 在类里面声明,类外面定义void Pop(){assert(_top > 0);--_top;}bool Empty(){return _top == 0;}const T& Top(){assert(_top > 0);return _a[_top - 1];}
private:T* _a = nullptr;size_t _top = 0;size_t _capacity = 0;
};// 注意:类模板中函数放在类外进行定义时,需要加模板参数列表
template<class T>
void Stack<T>::Push(const T& x)
{if (_top == _capacity){size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2; // 开新空间						T* tmp = new T[newCapacity];if (_a){memcpy(tmp, _a, sizeof(T)*_top); // 拷贝数据delete[] _a; // 释放旧空间}_a = tmp;_capacity = newCapacity;}_a[_top] = x;++_top;
}

注意:函数 / 类模板不支持分离编译,比如:声明放在 .h ,定义放在 .cpp。 在 .h 里实例化了,Stack.cpp 里没有实例化,而 test.cpp 去找的时候,只有声明,没有定义,会报链接错误如果声明和定义分离,需要将模板写在同一个文件里。


2、类模板的实例化

类模板实例化与函数模板实例化不同, 类模板实例化 需要在 类模板名字后跟 <> ,然后 实例化的类型放在 <> 中 即可,类模板名字不是真正的类,而实例化的结果才是真正的类
Vector<int> s1;
Vector<double> s2;
// 注意:Vector不是具体的类,是编译器根据被实例化的类型生成具体类的模具

Vector 是类名,Vector<int> 才是类型。

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

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

相关文章

高層建築設計和建造:從避難層到設備間和防風防火防水的設計理念,酒店住宅辦公樓都有什麽房間(精簡)

樓層概覽 標準層居住、辦公、商業等功能的樓層。結構和裝修與其他樓層相同&#xff0c;可供人正常居住、工作和活動避難層專門用於人員避難的樓層&#xff0c;通常會相隔數十個標準層&#xff0c;樓梯通常和標準層是錯開的(非公用)&#xff0c;具有更多的通風口。牆體和樓板具…

【Linux】TCP的服务端 + 客户端

文章目录 &#x1f4d6; 前言1. 服务端基本结构1.1 类成员变量&#xff1a;1.2 头文件1.3 初始化&#xff1a;1.3 - 1 全双工与半双工1.3 - 2 inet_aton1.3 - 3 listen 2. 服务端运行接口2.1 accept&#xff1a;2.2 服务接口&#xff1a; 3. 客户端3.1 connect&#xff1a;3.2 …

代谢组学分析平台(二)

GC/MS分析生物样本为何要衍生化处理&#xff1f;有哪些衍生化的方法&#xff1f; GC的流动相为气体&#xff08;通常为高纯氦&#xff09;&#xff0c;这就要求被分析物必须能够气化&#xff0c;而生物样本中很多内源性代谢物都含有极性基团&#xff0c;具有沸点高、不易气化特…

linux 笔记 安装 anaconda

1 找到anaconda 安装包 Free Download | Anaconda 2 在linux环境中安装对应安装包 3 安装完毕后查看是否安装好 发现不行&#xff0c;需要配置环境变量 4 配置环境变量 vim /etc/profile使用这个&#xff0c;发现对应的文件是只读文件 sudo vim /etc/profile在最下面加一…

【数据结构与算法】- 数组

数组 1.1 数组的定义1.2 数组的创建1.3 数组在内存中的情况2.1 初始化数组2.2 插入元素2.3 删除元素2.4 读取元素2.5 遍历数组 1.1 数组的定义 数组中的是在内存中是连续存储的&#xff0c;内存是由一个个内存单元组成的&#xff0c;每一个内存单元都有自己的地址&#xff0c;…

Linux 安全 - Credentials

文章目录 一、简介1.1 Objects1.2 Object ownership1.3 The objective context1.4 Subjects1.5 The subjective context1.6 Actions1.7 Rules, access control lists and security calculations 二、Types of Credentials2.1 Traditional UNIX credentials2.2 Capabilities2.3 …

【iptables 实战】05 iptables设置网络转发实验

一、网络架构 实验效果&#xff0c;通过机器B的转发功能&#xff0c;将机器A的报文转发到机器C 本实验准备三台机器分别配置如下网络 机器A ip:192.168.56.104 机器C ip:10.1.0.10 机器B 两张网卡&#xff0c;分别的ip是192.168.56.106和10.1.0.11 如图所示 如下图所示 二、…

PPT/PS——设置图片的背景为透明色的方法

方法一 将图片放到PPT内&#xff0c;点击图片&#xff0c;点击格式&#xff0c;删除背景 方法二 PS小白 扣去背景流程PS怎么把背景色改成透明色呢&#xff1f;

18年,51cto老师录视频- Vue.js前端开发基础与项目实战的接口,不能用了

18年&#xff0c;51cto老师录视频的接口&#xff0c;不能用了 获取商品列表 http://api.mm2018.com:8090/api/goods/home 23年&#xff0c;51cto老师录视频的接口&#xff0c;和老师新要的 获取商品列表 http://api.mm2018.com:8095/api/goods/home 请求无参&#xff1a; 响…

C#打包表情图片至Dll文件中供winform中调用

首先参照了这篇文章&#xff1a;C# 图片打包成DLL文件进行调用_png转dll-CSDN博客&#xff0c;感谢大神&#xff01; 想在winform里做个表情选择的界面&#xff0c;如果表情图片直接放在本地的话会造成更改或删除&#xff0c;给程序带来不确定的因素&#xff0c;还是直接把图片…

CSS 简介

你需要具备的知识 在继续学习之前&#xff0c;你需要对下面的知识有基本的了解&#xff1a; HTML / XHTML 什么是 CSS? CSS 指层叠样式表 (Cascading Style Sheets)样式定义如何显示 HTML 元素样式通常存储在样式表中把样式添加到 HTML 4.0 中&#xff0c;是为了解决内容与…

C++模拟题[第一周-T1] 扑克

[第一周-T1] 扑克 题目描述 斗地主是一种使用 A \tt A A 到 K \tt K K 加上大小王的共 54 54 54 张扑克牌来进行的游戏&#xff0c;其中大小王各一张&#xff0c;其它数码牌各四张。在斗地主中&#xff0c;牌的大小关系根据牌的数码表示如下&#xff1a; 3 < 4 < 5 …

npm ,yarn 更换使用国内镜像源,淘宝源

背景 文章首发地址 在平时开发当中&#xff0c;我们经常会使用 Npm&#xff0c;yarn 来构建 web 项目。但是npm默认的源的服务器是在国外的&#xff0c;如果没有梯子的话。下载速度会特别慢。那有没有方法解决呢&#xff1f; 其实是有的&#xff0c;设置国内镜像即可&#x…

c++学习之优先级队列

目录 1.初识优先级队列 库中的实现 使用优先级队列 2.优先级队列的实现 3.仿函数 利用仿函数实现的优先级队列 迭代器区间构造&#xff08;建堆&#xff09; 1.初识优先级队列 如果我们给每个元素都分配一个数字来标记其优先级&#xff0c;不妨设较小的数字具有较…

怎么通过portainer部署一个vue项目

这篇文章分享一下今天通过docker打包vue项目&#xff0c;并使用打包的镜像在portainer上部署运行&#xff0c;参考了vue-cli和docker的官方文档。 首先&#xff0c;阅读vue-cli关于docker部署的说明 vue-cli关于docker部署的说明https://cli.vuejs.org/guide/deployment.html#…

网络层常见协议——IPV4、IPV6、ARP、ICMP、QoS

目录 1、IPV4 协议 IPV4 地址的组成&#xff1a; IPV4地址的分类&#xff1a; 关于多播和组播&#xff1a; 常见组播地址分类&#xff1a; 特殊的 IPV4 地址&#xff1a; 私有地址和公有地址&#xff1a; 私有地址的范围&#xff1a; 子网划分&#xff1a; 子网掩码&…

操作系统--分页存储管理

一、概念介绍 分页存储&#xff1a;一是分内存地址&#xff0c;二是分逻辑地址。 1.分内存地址 将内存空间分为一个个大小相等的分区。比如&#xff0c;每个分区4KB。 每个分区就是一个“页框”&#xff0c;每个页框有个编号&#xff0c;即“页框号”&#xff0c;“页框号”…

【统计学】Top-down自上而下的角度模型召回率recall,精确率precision,特异性specificity,模型评价

最近在学 logistic regression model&#xff0c;又遇见了几个之前的老面孔。 召回率recall, 精确率precision&#xff0c;特异性spcificity&#xff0c;准确率accuracy&#xff0c;True positive rate&#xff0c;false positive rate等等名词在学习之初遇到的困难在于&#x…

【Java每日一题】— —第十八题:求二维数组中的元素最小值及其索引。(2023.10.02)

&#x1f578;️Hollow&#xff0c;各位小伙伴&#xff0c;今天我们要做的是第十八题。 &#x1f3af;问题&#xff1a; 求二维数组中的元素最小值及其索引。 测试结果如下&#xff1a; &#x1f3af; 答案&#xff1a; int [][]anew int[3][];a[0]new int [3];a[1]new int[5…

UG NX二次开发(C#)- 制图(Draft)-工程图框选制图曲线并输出制图曲线的信息

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 1、前言2、在UG NX中打开一个装配体模型3、进入工程制图模块,创建工程制图4、在VS中创建一个工程项目5、在Main()中添加选择的代码(UFun)6、在Main()中添加选择的代码(NXOpen)7、框选解决方案…