【C++基础(十)】C++泛型编程--模板初阶

💓博主CSDN主页:杭电码农-NEO💓

⏩专栏分类:C++从入门到精通⏪

🚚代码仓库:NEO的学习日记🚚

🌹关注我🫵带你学习C++

🔝🔝


模板

  • [1. 前言](#1. 前言 "#1__15")
  • [2. 函数模板](#2. 函数模板 "#2___46")
  • [3. 函数模板原理](#3. 函数模板原理 "#3__99")
  • [4. 函数模板实例化](#4. 函数模板实例化 "#4__110")
  • [5. 函数模板参数的匹配规则](#5. 函数模板参数的匹配规则 "#5__162")
  • [6. 类模板](#6. 类模板 "#6__189")
  • [7. 类模板的实例化](#7. 类模板的实例化 "#7__233")
  • [8. 总结以及拓展](#8. 总结以及拓展 "#8__278")

1. 前言

在学习数据结构时会遇见以下的情况

数据结构中存储的类型往往不能确定

所以在实现数据结构时往往是这样做的

cpp 复制代码
typedef int DateType

在写代码时用DateType来表示类型
如果想存储浮点型只需将int改为float

但是这样写会遇见一个问题:

写好数据结构类后在创建对象时

此.cpp文件只能创建一种类型的对象

对象存储的全是int/char/double类型
不能同时创建存储int的和char的对象

cpp 复制代码
Data d1;//存储的int类型
Date d2;//存储的char类型

泛型编程:
编写与类型无关的通用代码
是代码复用的一种手段
模板是泛型编程的基础

本章重点:

本篇文章重点讲解函数模板
和类模板的使用以及特性


2. 函数模板

请看以下函数代码:

cpp 复制代码
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;
}

这样写非常的麻烦

使用模板可以使代码通用于不同类型:
swap函数模板:

cpp 复制代码
template<typename T>
void Swap( T& left, T& right)
{
	T temp = left;
	left = right;
	right = temp;
}

写好上面的代码后,传int类型进去
T就会被实例化为int,以此类推

template和typename是规定
好了必须这样写,T是自己取的名字
其中,typename可以用class替换
并且一次性可以定义多个类型:

cpp 复制代码
template<class T1,typename T2,class T3>

3. 函数模板原理

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

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

可以用下面这张图来理解:


4. 函数模板实例化

  1. 隐式实例化

让编译器根据实参推演模板参数实际类型

cpp 复制代码
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);
 }

第一次调用的T被推演为int类型
第二粗调用的T被推演为double

不能这样写代码:

cpp 复制代码
Add(a1, d1);

系统根据a1推演出T是int类型
但是d1是double类型不能用int
类型的参数啦接受,所以会报错


  1. 显示实例化

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

cpp 复制代码
int main(void)
{
 int a = 10;
 double b = 20.0;
 
 // 显式实例化
 Add<int>(a, b);
 return 0;
}

如果类型不匹配
编译器会尝试进行隐式类型转换
若无法转换成功编译器将会报错


5. 函数模板参数的匹配规则

模板函数和普通函数可以同时存在:

cpp 复制代码
// 专门处理int的加法函数
int Add(int left, int right)
{
 	return left + right;
}
// 通用加法函数
template<class T>
T Add(T left, T right)
{
 	return left + right;
}

在调用函数时若参数和非模板函数匹配
那么编译器会优先调用非模板函数
若非模板函数不匹配或模板函数更匹配
那么编译器会优先调用模板函数

cpp 复制代码
Add(10,20)//调用非模板
Add(11.1,6.3);//调用模板

6. 类模板

类模板的应用非常广泛

像开头提到的数据结构问题

类模板的定义格式:

cpp 复制代码
template<class T1, class T2, ..., class Tn>
class example
{
 // 类内成员定义
};

和函数模板类似,类模板也可以同时

定义多个模板参数

写一个简易的顺序表:

cpp 复制代码
template<class T>
class Vector
{ 
public :
 Vector(size_t capacity = 10)
 : _Data(new T[capacity])
 , _size(0)
 , _capacity(capacity)
 {}
 T& operator[](size_t pos)
 {
	 assert(pos < _size);
	 return _Data[pos];
 }
 
private:
 T* _Data;
 size_t _size;
 size_t _capacity;
};

所有实际类型需要出现的地方用T代替


7. 类模板的实例化

和函数模板不同,类模板没有隐式推演
用户必须显示实例化

cpp 复制代码
Vector<int> v1;

注意:
Vector是类名
Vector< int >才是类型

当在类中声明一个函数
但是想在类外定义时
若函数的参数或内部使用的类型
和模板有关系,那么必须这样写:

cpp 复制代码
template<class T>
class Vector
{ 
public :
    //类中声明函数
	void push_back(T x);
private:
 T* _Data;
 size_t _size;
 size_t _capacity;
};

类外定义:

cpp 复制代码
template<class T>
void Vector<T>::push_back(T x)
{
	_Date[_size] = x;
	_size++;
}

注:必须要再加上类模板template
并且要指定类域


8. 总结以及拓展

泛型编程是C++的一大利器

它极大的减少了代码的复杂程度

并且增加了代码的可读性

C++基础部分的内容已经全部结束
下一阶段进入C++中阶:STL的使用

拓展:

泛型编程拓展阅读


🔎 下期预告:STL库的介绍以及使用 🔍

相关推荐
2035去旅行4 小时前
bootcmd 详细讲解
linux·嵌入式·arm·soc
陈希1234 小时前
Linux 防火墙配置实战:firewalld、iptables 与云安全组
linux·运维·服务器
女神下凡5 小时前
芯参谋(95):嵌入式文件系统_开发指南
linux·服务器·arm开发·驱动开发·设计规范
且从容.9 小时前
Ubuntu安装flume
linux·ubuntu·flume
承渊政道9 小时前
Linux网络学习【Reactor反应堆模式】
linux·网络·学习·ubuntu·reactor
且从容.12 小时前
ubuntu安装flink
linux·ubuntu·flink
爱和冰阔落13 小时前
【Linux网络】TCP 服务器如何并发处理多个客户端?从 fork、僵尸进程到双重 fork
linux·运维·服务器·网络
程序员沉梦听雨13 小时前
WSL2的安装与使用
linux·wsl
微小的鱼14 小时前
Linux的渲染与显示系统链路
android·java·linux
lisanmengmeng14 小时前
alertmanger 标签 rules 打上分组标签
linux·运维·服务器