模板初阶学习

简介: 模板初阶学习

1 初识泛型编程

我们之前学过一个交换的Swap函数,可以交换两个变量之间的值!但是我们后续在使用过程中就会发现,如果交换的两个变量的类型改变了,我们就必须要在写过一份!这样代码的复用率就太低了!那么我们在C++中为了解决这种代码复用率较低的问题。引用泛型编程这一概念!


泛型编程:编写一个与类型无关的通用代码!让编译器自己进行识别!这种是代码复用常见的手段!模板就是泛型编程的基础!


模板就可以简单理解为是通用的代码!以Swap函数为例:就是可以根据你传入的数据类型,编译器会生成对应类型的交换函数!模板可以分为:函数模板和类模板


2 函数模板

2.1 概念

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

template<typename T,typename X……>
返回值类型 函数名(参数列表){}

template表示这是一个模板,typename关键字是用来定义模板参数的关键字!也可以使用class关键字。但是不能用struct来代替class关键字!则我们可以写出Swap函数的模板:

template<typename T>

void Swap(T& x,T& y)
{
  T tmp = x;
  x = y;
  y = tmp;
}

int main()
{
  int a = 10;
  int b = 20;
  Swap(a,b);
  char x = '0';
  char y = '9';
  Swap(x, y);
  return 0;
}

2.2 原理

看起来我们使用的是同一份代码,但实际上我们用的不是同一份代码!在编译器编译阶段,编译器会根据传入的实参类型来推演生成我们所需要类型的Swap函数,比如说我们传入的实参是int类型那么此时编译器就会将模板参数换成int,从而调用!

2.3 函数模板的实例化

函数模板的实例化分为隐式实例化和显式实例化

上面我们所写的关于Swap函数,自己传入实参,由编译器自己进行识别推演的,我们称为隐式实例化!下面我们来介绍一下什么是显式实例化:

在函数名后的<>中指定模板参数类型

我们先来看这样一段代码:

template <typename P>
int ADD(P x,P y)
{
  return x + y;
}
int main()
{
  int a = 10;
  double b = 20.0;
  int ret = ADD(a, b);
  return 0;
}

在编译的时候就会出现报错,发现如下问题:

这是因为在调用ADD函数时,编译器不知道是应该把int转换成double,还是把double转换成int!此时我们可以利用模板的显式实例化!

int ret = ADD<int>(a, b);

指明要将double转换成int类型!

2.4 模板参数的匹配原则

1️⃣一个非模板函数可以与一个同名的函数模板可以一起存在,并且该函数模板还是可以实例化生成同名非模板函数

int ADD(int a, int b)
{
  return a + b;
}
template <typename P>
int ADD(P x,P y)
{
  return x + y;
}
int main()
{
  int a = 10;
  int b = 20;
  ADD(a, b);//调用同名的非模板函数
  ADD<int>(10, 20);//编译器会调用特化的模板函数
  return 0;
}

2️⃣如果条件都相同,对于非模板函数与模板函数,会优先调用非模板函数,而不会从模板函数中在生成一个实例出来!如果模板函数能够产生一个更好匹配的函数,那么就会优先调用模板!

int ADD(int a, int b)
{
  return a + b;
}
template <typename P1,typename P2>
int ADD(P1 x, P2 y)
{
  return x + y;
}

int main()
{
  int a = 10;
  int b = 20;
  double c = 20.0;
  ADD(a, b);//优先调用非模板函数
  ADD(a, c);//优先调用模板函数

  return 0;
}

3️⃣模板函数不支持自动类型转换(隐式的类型转换,比如把double转换成int),普通函数支持自动类型转换!


3 类模板

格式类型:

template<class T1, class T2, …, class Tn>

class 类模板名

{

// 类内成员定义

};

之前我们在C语言中学过typedef关键字,我们只需要改变typedef后的数据类型,就可以更改类中所要存储的数据类型,代码如下:

typedef int Date;

class Stack {
private:
  Date* _a;
  int _capacity;
  int _top;
public:
  Stack(int capacity = 10){
    _a = new Date[capacity];
    _capacity = capacity;
    _top = 0;
  }
};

但是,这也是有局限性的,因为如果需要多个栈并且多个栈中的存储的数据类型不一样,我们也要多写几分类似的代码!类模板就可以很好的解决这个问题!

template <class T>
class Stack
{
private:
  T* _a;
  int _capacity;
  int _top;

public:
  Stack(int capacity = 4)
  {
    _a = new T[capacity];
    _top = 0;
    _capacity = capacity;
  }
};

类模板的实现原理和函数模板的原理是一样的,但是类模板是通过显示实例化,而不是让编译器自己进行推演!

int main()
{
  Stack<int> st1;//放int的栈
  Stack<double> st2;//放double的栈
  Stack<char> st3;//放char类型的栈

  return 0;
}

注意:通过类模板实例化的类不是和普通类一样,普通类的类名就是类型,实例化的类就是类名<数据类型>才是类型!

类模板中函数的声明与定义写法如下所示:

template <class T>
class Stack
{
private:
  T* _a;
  int _capacity;
  int _top;

public:
  Stack(int capacity = 4);
};

template <class T>
Stack<T>::Stack(int capacity = 4)//指定类型作用域
{
  _a = new T[capacity];
  _capacity = capacity;
  _top = 0;
}
目录
相关文章
|
Web App开发 缓存 应用服务中间件
【CDN 最佳实践】获取历史脏数据的处理思路及规避方法
保证提供给客户端准确的数据是保障服务质量的最基本的要求。但是在实际使用过程中经常会发现客户端访问到的数据为历史脏数据影响客户体验。本文将在包括 CDN 的场景下分析常见出现该问题的原因以及规避方法,希望大家可以在遇到该问题时可依据该思路尽快恢复解决。
4744 0
|
监控 算法 机器人
软件体系结构 - 调度算法(2) 最低松弛度优先
【4月更文挑战第19天】软件体系结构 - 调度算法(2) 最低松弛度优先
431 0
|
12月前
|
Python
用python3快速读取30G+的txt文件
这篇文章介绍了如何使用Python分块读取大文件(如30G+的txt文件),通过设置每次读取的块大小来处理大型文本文件,以减少内存消耗并提高处理效率。
291 14
|
NoSQL MongoDB Windows
MongoDB 读写分离——Windows MongoDB 副本集配置
MongoDB 读写分离——Windows MongoDB 副本集配置
281 0
|
11月前
|
JavaScript 编译器 数据安全/隐私保护
TypeScript :关键字
本文介绍了 TypeScript 中的一些核心类型和工具类型,包括 `interface` 和 `type` 的基本使用和区别,以及一些高级类型如 `keyof`、`Record`、`Pick`、`Partial`、`Readonly` 和 `Omit` 的使用方法。文章还详细解释了 `namespace` 的作用和使用场景,帮助开发者更好地组织和管理代码,避免命名冲突,并提高代码的可维护性和可读性。
156 1
|
设计模式 Java 测试技术
分层设计:Service 层真的需要实现接口吗?
【8月更文挑战第4天】在软件开发领域,分层设计是一种广泛应用且高效的设计模式,它通过将系统划分为不同的逻辑层(如表现层、服务层、数据访问层等),来提高代码的可维护性、可扩展性和可测试性。其中,Service层作为业务逻辑处理的核心,其设计尤为重要。那么,Service层是否真的需要实现接口呢?这个问题值得我们深入探讨。
381 8
|
缓存 前端开发 JavaScript
优化前端性能的五种最佳实践
在现代 web 开发中,前端性能的优化是提高用户体验的关键。本文探讨了五种最佳实践来提升前端性能,包括代码分割、懒加载、压缩资源、优化渲染和使用缓存策略。这些方法不仅有助于减少加载时间,还能提高应用的响应速度和整体用户满意度。
|
Kubernetes 安全 Linux
在k8S中,Calico网络组件实现原理是什么?
在k8S中,Calico网络组件实现原理是什么?
|
机器学习/深度学习 数据采集 自然语言处理
大语言模型系列:Transformer
大语言模型系列:Transformer
1026 0