learn C++ NO.8——初识模板(函数模板、类模板)

news2024/9/24 9:20:53

文章目录

  • 引言
  • 1.泛型编程
    • 1.1.什么是泛型编程?
  • 2.函数模板
    • 2.1.什么是函数模板
    • 2.2.为什么需要函数模板
    • 2.3.函数模板格式
    • 2.4.函数模板实现原理
    • 2.5.函数模板的实例化
  • 3.类模板
    • 3.1.类模板定义格式
      • 3.1.1.类模板语法
      • 3.1.2.模板类的定义
    • 3.2.模板类的实例化

引言

现在是北京时间2023年6月5号13.31分,距离上一篇博客发布已过一周。期间还零零散散进行了一些期末考试,这也说明了我的大一时光快要结束了。我也想抓着期末的尾巴,好好的复习一下前面的所学内容,争取这周能够更一下简单数据结构的博客。

1.泛型编程

1.1.什么是泛型编程?

泛型编程是一种编程范式,它可以让代码更加通用和灵活。它通过使用类型参数来实现代码的重用,而不是为每种类型编写不同的代码。具体来说,泛型编程允许我们编写可以适用于多种类型的代码,而不必为每种类型都编写一套代码。这样可以提高代码的复用性和可维护性,同时也可以减少代码的冗余和错误。举一个生活中的例子,泛型编程其实是类似于格式化的生产工艺品,只要有一个模具,就可以通过这股模具来大批量生产一样的东西,使生产效率上升。而模板可以复用代码,根据模板参数来适配不同的类型,使得一套通用代码可以适配多种场景。编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。

在这里插入图片描述

2.函数模板

2.1.什么是函数模板

函数模板是一种通用的函数定义,可以用来定义多个具有相同基本结构但不同数据类型的函数。函数模板是通过在函数定义中使用一个或者多个类型参数来实现的。类型参数可以用来定义函数参数和返回值的数据类型,以及函数体内使用的变量和常量的数据类型。函数模板可以用于任意的数据类型,从而提高了代码的复用性和可读性。在实际的编程中,函数模板可以用于实现通用算法、容器类和其他的常用数据结构。

2.2.为什么需要函数模板

请看下面的场景,假设需要多个类型的变量进行交换操作,在前面的c语言和c++的代码写法是这样的。

// C
void SwapInt(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

void SwapDou(double* x, double* y)
{
	double tmp = *x;
	*x = *y;
	*y = tmp;
}

// CPP -->(函数重载)

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

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


可以看到上面的代码的重复率很高,而且只有新需要的类型就要手动添加一份,非常麻烦。其次就是代码不方便维护,有一点错每份代码都要跟着修改,非常的不方便。于是乎我们的c++祖师爷们就整出了函数模板。下面正式介绍函数模板。

2.3.函数模板格式

//两种都可以
template<typename T1,typename T2,...>
//template<class T1,class T2,...>
返回类型 函数名(参数)函数定义

首先,需要写关键字template,后面紧跟一对尖括号。尖括号内部存放的是类型参数。然后换行写函数的定义。下面我以Swap函数来举例。


template<typename T>
void Swap(T& t1, T& t2)
{
	T tmp = t1;
	t1 = t2;
	t2 = tmp;
}

在这里插入图片描述

2.4.函数模板实现原理

模板函数在被调用时,编译器会根据不同的数据类型,生成对应的函数实例。编译器会根据实际传入的参数类型,选择合适的函数实例进行调用。模板函数其实是类似我们能够生成各个颜色手机壳的那个模具,而参数类型类似于手机壳的颜色。当我们需要红色的手机壳,就将材料导入模具中并加上红色颜料,便得到了红色的手机壳。
在这里插入图片描述

通过调试转到反汇编查看汇编可以看到,编译器确实会自动生成对应数据类型的模板函数实例,并调用模板函数实例。

2.5.函数模板的实例化

用各种不同类型的参数去调用函数模板去实例化时,这一行为叫做函数模板的实例化。函数模板实例化分为两种,分别是显示实例化和自动推导类型实例化。下面就通过代码来举例。

  1 #include<iostream>  
  2   
  3 using namespace std;  
  4   
  5 template<typename T>  
  6 T Add(const T& x, const T& y)  
  7 {  
  8   return x + y;  
  9 }                                                                             
 10                                                                               
 11 int main()                                                                    
 12 {                                                                             
 13   int i1 = 10;                                                                
 14   int i2 = 20;                                                                
 15   double d1 = 3.14;                                                           
 16   double d2 = 1.23;                                                           
 17   
 18   //编译器自动推导  
 19   cout << Add(i1,i2) << endl;  
 20   cout << Add(d1,d2) << endl;
 21   cout << Add(i1,(int)d2) << endl;  
 22   cout << Add((double)i1,d2) << endl;  
 23                
 24  //显示实例化
 25  
 26   cout << Add<double>(d1,d2) << endl;
 27   cout << Add<int>(d1,d2) << endl;
 28                                                                                        
 29   return 0;
 30 }          

在这里插入图片描述

对于这里模板的模板参数需要用const修饰呢?这是因为int类型的参数提升成double类型参数会产生一个临时变量。而临时变量具有常属性,这里的const修饰模板参数后,可以避免权限放大的问题而导致编译器报错。 为什么编译器可以自动推导类型还需要显示实例化呢?请看下面的样例演示。

#include<iostream>

using namespace std;


template<typename T>
T* MyAlloc(size_t sz)
{
  return new T[sz];
}

int main()
{
  //必须显示实例化调用函数模板
  int* pi = MyAlloc<int>(10);
  return 0;
}

3.类模板

在没有类模板之前,使用C语言编写一些容器需要重复大量的模块。因为,C语言不支持类模板,这也是C语言没法提供标准容器库。类模板的出现标志着C++进入了一个新的时代,因为它带来的是STL标准库的横空出世。

//C语言
typedef int DataType;
class Stack
{
public:
    Stack(size_t capacity = 3)
    {
        _array = (DataType*)malloc(sizeof(DataType) * capacity);
        if (NULL == _array)
        {
            perror("malloc申请空间失败!!!");
            return;
        }

        _capacity = capacity;
        _size = 0;
    }

    void Push(DataType data)
    {
        // CheckCapacity();
        _array[_size] = data;
        _size++;
    }

    // 其他方法...

    ~Stack()
    {
        if (_array)
        {
            free(_array);
            _array = NULL;
            _capacity = 0;
            _size = 0;
        }
    }

private:
    DataType* _array;
    int _capacity;
    int _size;
};

如果我们在一个模块里既要用栈存int类型的数据,又要用栈存double类型的数据。该怎么办呢?

class StackInt
{
public:
    StackInt(size_t capacity = 3)
    {
        _array = (int*)malloc(sizeof(int) * capacity);
        if (NULL == _array)
        {
            perror("malloc申请空间失败!!!");
            return;
        }

        _capacity = capacity;
        _size = 0;
    }

    void Push(int data)
    {
        // CheckCapacity();
        _array[_size] = data;
        _size++;
    }

    // 其他方法...

    ~StackInt()
    {
        if (_array)
        {
            free(_array);
            _array = NULL;
            _capacity = 0;
            _size = 0;
        }
    }

private:
    int* _array;
    int _capacity;
    int _size;
};

class StackDouble
{
public:
    StackDouble(size_t capacity = 3)
    {
        _array = (double*)malloc(sizeof(double) * capacity);
        if (NULL == _array)
        {
            perror("malloc申请空间失败!!!");
            return;
        }

        _capacity = capacity;
        _size = 0;
    }

    void Push(double data)
    {
        // CheckCapacity();
        _array[_size] = data;
        _size++;
    }

    // 其他方法...

    ~StackDouble()
    {
        if (_array)
        {
            free(_array);
            _array = NULL;
            _capacity = 0;
            _size = 0;
        }
    }

private:
    double* _array;
    int _capacity;
    int _size;
};

int main()
{
	StackInt SI;//存int类型
	StackDouble SD;//存double类型
	return 0;
}

这样实在是太麻烦了,不仅代码不容易维护,而且对编程效率有着极大的影响。所以我们的祖师爷们整出了类模板。类模板其实就是让编译器可以根据我们的需求实例化出各种类型的类,大大提高了我们的效率。

3.1.类模板定义格式

3.1.1.类模板语法

template<class T,class T2,...>
class classname
{
	//类成员
};

注意:普通类的类名就是类的类型。模板类的的类名就是类名本身,而模板类的类型是类名<模板参数>

3.1.2.模板类的定义

// 类模板
template<class T>
class Stack
{
public:
	Stack(size_t capacity = 3);

	void Push(const T& data);

	// 其他方法...

	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}

private:
	T* _array;
	int _capacity;
	int _size;
};


template<class T>
Stack<T>::Stack(size_t capacity)
{
	/*_array = (T*)malloc(sizeof(T) * capacity);
	if (NULL == _array)
	{
		perror("malloc申请空间失败!!!");
		return;
	}*/
	_array = new T[capacity];

	_capacity = capacity;
	_size = 0;
}

template<class T>
void Stack<T>::Push(const T& data)
{
	// CheckCapacity();
	_array[_size] = data;
	_size++;
}

模板类的成员函数声明定义分离时,需要在每个成员函数声明函数模板。并制定对应的类模板的类型。

3.2.模板类的实例化

类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<> 中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。

Stack<int> S1;
Stack<double> S2;

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

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

相关文章

Hadoop之Yarn概述

Hadoop之Yarn概述 Yarn是什么Yarn基础架构Yarn工作机制回顾HDFS、YARN、MapReduce三者关系Yarn调度器和调度算法先进先出调度器&#xff08;FIFO&#xff09;容量调度器&#xff08;Capacity Scheduler&#xff09;公平调度器&#xff08;Fair Scheduler&#xff09; Yarn常用命…

SpringBoot整合SpringSession实现分布式登录详情

目录 Session 共享为什么服务器 A 登录后&#xff0c;请求发到服务器 B&#xff0c;不认识该用户&#xff1f;解决方案SpringBoot整合SpringSession实现分布式登录 Session 共享 比如两个域名&#xff1a; aaa.yupi.combbb.yupi.com如果要共享 cookie&#xff0c;可以种一个…

事件机制原理剖析及实际业务应用说明

什么是事件&#xff1f; 一个特定的场景发生了一个特定的情况就是一个事件。 事件在设计中的作用 为对象之间解耦。 举例 现有用户中心和消息中心。 目前&#xff0c;有一个用户注册的场景&#xff0c;此场景要求用户注册成功后要给用户发送多渠道欢迎通知&#xff08;微信、…

(11) XGBoost

文章目录 1 简要介绍2 梯度提升树2.1 提升集成算法&#xff1a;重要参数n_estimators2.2 有放回随机抽样&#xff1a;重要参数subsample2.3 迭代决策树&#xff1a;重要参数 η \eta η 3 XGBoost的智慧3.1 选择弱评估器&#xff1a;重要参数booster3.2 目标函数&#xff1a;重…

SpringCloud_微服务基础day2(Eureka注册中心:服务注册与发现

p6:Eureka简介与依赖导入 前面我们了解了如何对单体应用进行拆分&#xff0c;并且也学习了如何进行服务之间的相互调用&#xff0c;但是存在一个问题&#xff0c;就是虽然服务拆分完成&#xff0c;但是没有一个比较合理的管理机制&#xff0c;如果单纯只是这样编写&#xff0c…

HBase:(三)HBase API

HBase:(一)安装部署_只爱大锅饭的博客-CSDN博客hbase部署安装https://blog.csdn.net/qq_35370485/article/details/130988364?spm1001.2014.3001.5501 1.创建maven项目 2.添加依赖 <dependency><groupId>org.apache.hbase</groupId><artifactId>hba…

【鲁棒】对信息不完整的 DSGE 模型进行鲁棒预测(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

使用 TiUP 部署 TiDB 7.1.0 集群

使用 TiUP 部署 TiDB 7.1.0 集群 参考官方文档 1. 下载社区版安装包 TiDB 社区版 TiDB 7.1.0 为长期支持版本 (Long-Term Support Releases, LTS)。 TiDB-community-server 软件包 TiDB-community-toolkit 软件包 wget https://download.pingcap.org/tidb-community-ser…

【论文阅读】Attention-based Deep Multiple Instance Learning

misc{Ilse_Tomczak_Welling_2018, title{Attention-based Deep Multiple Instance Learning}, journal{International Conference on Machine Learning}, author{Ilse, Maximilian and Tomczak, JakubM. and Welling, Max}, year{2018}, month{Jul} }1、摘要与引言 本文…

CodeForces..走路的男孩.[简单].[时间间隔]

题目描述&#xff1a; 题目解读&#xff1a; 数学上的遛狗问题。 大意就是需要遛狗&#xff0c;一天至少两次&#xff0c;每次至少120分钟&#xff0c;题中所给的时间轴是从0-1440分钟&#xff0c;表示一整天。 然后它的主人正在不断接收信息&#xff08;在工作&#xff09;…

作用域 (局部作用域和全局作用域) 详细介绍

作用域 (局部作用域和全局作用域) 详细介绍 作用域是当前的执行上下文&#xff0c;值和表达式在其中“可见”或可被访问。 常见的作用域为&#xff1a; 全局作用域&#xff1a;脚本模式运行所有代码的默认作用域 函数作用域&#xff1a;由函数创建的作用域 局部作用域&#xff…

附录1-小程序常用标签

目录 1 view 2 scroll-view 3 swiper与swiper-item 4 text 5 rich-text 6 button 7 image 1 view 相当于html的div 2 scroll-view scroll-view是一个有滚动条的div scroll-y是允许纵向滚动&#xff0c;scroll-x是允许横向滚动 3 swiper与swiper-item swipe…

文盘Rust -- tokio绑定cpu实践 | 京东云技术团队

tokio 是 rust 生态中流行的异步运行时框架。在实际生产中我们如果希望 tokio 应用程序与特定的 cpu core 绑定该怎么处理呢&#xff1f;这次我们来聊聊这个话题。 首先我们先写一段简单的多任务程序。 use tokio::runtime; pub fn main() {let rt runtime::Builder::new_mu…

华为OD机试真题 Java 实现【通信误码】【2022Q4 100分】,附详细解题思路

一、题目描述 信号传播过程中会出现一些误码&#xff0c;不同的数字表示不同的误码ID&#xff0c;取值范围为1~65535&#xff0c;用一个数组记录误码出现的情况。 每个误码出现的次数代表误码频度&#xff0c;请找出记录中包含频度最高误码的最小子数组长度。 二、输入描述 …

Nginx+花生壳实现个人网站

下载Nginx Windows版本&#xff0c;目前稳定版1.24&#xff0c;Mainline的版本是1.25 Nginx下载后是一个zip文件&#xff0c;选择适当的位置解压。 首先修改一下Nginx的配置文件nginx.conf&#xff0c;位于conf目录下面&#xff1a; 配置文件我只修改了访问的端口号&#xff…

文件操作之文件包含全解(31)

文件包含的作用就是将这个文件包含进去之后&#xff0c;会调用指定文件的代码。先将文件包含才能执行里面的一些相关代码&#xff0c;比如所想进行文件的链接&#xff0c;数据库的查询&#xff0c;就可以先包含一个数据库的配置文件&#xff0c; 再去链接的话就享有配置文件的一…

2023年6月北京/广州/深圳CDGA/CDGP数据治理认证招生

DAMA认证为数据管理专业人士提供职业目标晋升规划&#xff0c;彰显了职业发展里程碑及发展阶梯定义&#xff0c;帮助数据管理从业人士获得企业数字化转型战略下的必备职业能力&#xff0c;促进开展工作实践应用及实际问题解决&#xff0c;形成企业所需的新数字经济下的核心职业…

C#发送邮箱设置及源码

用C#调用发送邮箱代码之前需要邮箱开通SMTP/POP3及设置授权码&#xff0c;开通及获取方法如下&#xff1a; 1、打开邮箱&#xff0c;登录邮箱&#xff0c;进入设置&#xff0d;》帐户 2、在“帐户”设置中&#xff0c;找到服务设置项&#xff0c;进行设置&#xff0c;如下…

什么是软件测试?这份工作到底怎么样?

什么是软件测试&#xff1f;这份工作到底怎么样&#xff1f; 着人工智能时代的到来&#xff0c;IT行业受到了越来越多人的重视。软件测试作为把控软件质量必不可少的环节&#xff0c;其重要性可见一斑。 那么从事软件测试行业究竟有前途吗?今天我们就来详细的介绍一下软件测…

第34步 机器学习实战DLC:不平衡数据处理(上)

失踪人口回归的第一期&#xff0c;来说一说不平衡数据。 一、不平衡数据的概念 什么是不平衡数据&#xff1f;我们先来看看小Chat怎么说的&#xff1a; 顾名思义&#xff0c;就是你的因变量1和0的样本数差距有点大&#xff0c;大到影响了模型性能。举个栗子&#xff0c;一个盒…