【C++破局】泛型编程|函数模板|类模板

作者主页

📚lovewold少个r博客主页

   ⚠️本文重点c++模板初阶知识点讲解

👉【C-C++入门系列专栏】博客文章专栏传送门

😄每日一言:花有重开日,人无再少年

目录

前言

泛型编程

函数模板

函数模板概念

函数模板格式

函数模板的原理

函数模板的实例化

模板参数的匹配原则

类模板

类模板的定义格式

类模板的实例化

总结


前言

        C++是一门面向对象的语言,很多情况下我们不需要在编写程序时候去过多的考虑底层。而在前面我们学习C++的输入输出好像就是这样子,编译器会自动帮我们做很多的事情,而不需要自己去传递输入输出的变量类型等因素。这种方式肯定对于编写程序的人来讲是轻松的。世界上的各种科技的进步其实都离不开人对于懒惰的追求,对于方便的执着,而今天我要讲解的模板好像就算是一种特殊的产物。

        首先我们先写一个比较简单的程序来细致的探究一下我们为何需要模板。这里我们写了一个用于整形变量交换的函数,交换函数无论是在什么排序亦或者一些计算的时候都是常用函数。

void swap(int& x, int& y)
{
	int temp = x;
	x = y;
	y = temp;
}
int main()
{
	int a = 10;
	int b = 5;
	cout << "a=" << a << " " << "b=" << b << endl;
	swap(a, b);
	cout << "swap~" << endl;
	cout << "a=" << a << " " << "b=" << b << endl;
	return 0;
}

        问题来了,我们这里需要特别强调这里是用于整形类型的交换函数,因此这对于其他类型变量并不合适。那么我们如何去实现长整形,短整形,浮点型,字符型······。函数重载?

        C++提供了函数重载的方式,对于一些传参会直接以其传递参数而决定对应的函数。

        但是缺陷也很明显,我们的函数仅仅是类型不同,那么多类型我们都需要去重载么。亦或者我们先创建我们需要的类型,等到新类型出现的时候,用户再自己去增加对应的函数重载么。

        另一个关键点是,我们这里仅仅只是交换函数,比较简单,而对于一份各种函数相互嵌套的代码,一份代码出错,其他重载函数全得改,代码的可维护性比较低。

泛型编程

        我们再看先前的代码,会发现仅仅只是类型的不同罢了,我们可不可以提供一种方式和cout以及cin一样,把类型识别的任务交给编译器去完成,自己只需要给他传递参数变量即可。也就是说我们只需要提供一份代码作为模具,编译器可以根据不同的类型利用这个模具生成相应的代码。

void swap(int& x, int& y)
{
	int temp = x;
	x = y;
	y = temp;
}
void swap(double& x, double& y)
{
	double temp = x;
	x = y;
	y = temp;
}
void swap(char& x, char& y)
{
	char temp = x;
	x = y;
	y = temp;
}
int main()
{
	int a = 10;
	int b = 5;
	cout << "a=" << a << " " << "b=" << b << endl;
	swap(a, b);
	cout << "swap~" << endl;
	cout << "a=" << a << " " << "b=" << b << endl;
	return 0;
}

        无论是活字印刷术还是现在的模具浇筑技术,其根本的目的就是维持其功能一致即可,你可以注入不同的材料以改变其最后成品的效果,但本质上实现的功能是一样的,外形是一致的。

        之所以cv工程师能有独特的cv大法,也是因为前人拥有特定的已经可以使用的板子,而只需要去改吧改吧然后切合自己的工程内容即可实现一个全新的项目成果。这也就是一种代码复用的常规手段而已。

         话不多说,接下来直入正题。我们先谈一谈何为泛型编程。

        泛型编程是一种编程范式,其目标是编写与特定数据类型无关的通用代码,以便更广泛地重用代码。泛型编程使得程序员可以编写与数据类型无关的算法和数据结构,从而提高代码的灵活性、可重用性和可维护性。

        具体而言,泛型编程通过使用参数化类型(parameterized types)来实现。参数化类型是一种允许在代码中使用未指定具体类型的抽象类型。这样,可以编写算法和数据结构,而不必在编写时指定具体的数据类型。在需要使用这些算法和数据结构的地方,可以通过提供具体的类型来实现参数的具体化。

        在C++中,泛型编程主要通过模板来实现。模板允许程序员编写与数据类型无关的代码,可以用于不同的数据类型。这使得在不同的上下文中重用代码成为可能。例如,可以编写通用的排序算法、容器类、以及其他算法和数据结构,而不必为每种数据类型都编写一套特定的代码。而模板就是泛型编程的基础。

函数模板

函数模板概念

        函数模板是C++中用于创建通用函数的一种机制,允许程序员编写与特定数据类型无关的函数代码。函数模板通过使用参数化类型来实现,使得可以在编写代码时使用未指定具体类型的抽象类型这样,函数模板可以适用于多种数据类型,提高了代码的灵活性和重用性。

函数模板格式

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

template T 表示模板参数,T是一个占位符,代表任意数据类型。在函数模板中,可以使用T作为函数的参数类型、返回类型,以及在函数体中进行通用的操作。

注意:typename是用来定义模板关键字,也可以使用class不能使用struct代替class

函数模板的原理

        人类从农业时期到工业时期,很多重复机械的工作直接交给了机器去完成,极大的解放的生产力。机器生产淘汰了很多手工创作的东西,其本质上就是把这些工作交给了机器去完成。

        函数模板本身并不是一个函数,而是像类一样作为一个蓝图,是编译器用使用方式产生具体类型函数的一个模具。

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

函数模板的实例化

用不同类型的参数使用函数模板的时候称之为函数模板的实例化。模板参数实例化分为隐式实例化和显式实例化。

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

template <class T>
T Add(const T& left, const T& right)
{
	return left + right;
}

int main()
{
	int a1 = 10;
	int a2 = 5;
	Add(a1, a2);

	double b1 = 10.0;
	double b2 = 5.0;
	Add(b1, b2);
	
	return 0;
}

        而当调用的时候将不同类型混合传参是不被允许的,因为在编译期间编译器需要推演,对于a1可以推演为int,b1可以推演为double,但是模板参数列表中只有一个T,编译器无法确定T被换为int还是double而报错。在模板中,编译器一般不会去做类型转换,否则出错了编译器就会承担不小的后果。

        因此对于这种问题我们通常有两种方式进行解决:用户自己进行强制类型转换或者使用显示实例化

显示实例化:在函数名的后面指定模板参数的实际类型

int main()
{
	int a1 = 10;
	int a2 = 5;
	Add(a1, a2);

	double b1 = 10.0;
	double b2 = 5.0;
	Add(b1, b2);
	Add<int>(a1, b1);//显示实例化
	return 0;
}

模板参数的匹配原则

        一个非模板函数和模板函数可以同时存在,而且该模板函数还可以实例化为这个非模板函数。(这种实例化为非模板函数指在参数传递上可能维持一样,但是依据优先匹配的原则,在调用函数的时候可以做一些特殊处理)。这里我们通过不同函数之间打印信息而进行调用优先级的查看。

//专门处理整形类型加法函数
int Add(int left, int right)
{
	cout << "非模板函数" << endl;
	return left + right;
}
//通用加法模板
template<class T>
T Add(T left, T right)
{
	cout << "模板函数" << endl;
	return left + right;
}
void test()
{
	Add(1,2);//与非模板函数优先匹配
	Add(1.0, 2.0);//模板函数
	Add<int>(1, 2);//调用特定版本的Add版本,走模板函数
}
int main()
{
	test();
	return 0;
}

        对于非模板函数与同名函数模板,如果有其他条件相同,在调用的时候会优先去调用非函数模板而不会从该函数模板中生成实例化函数,如果一个函数可以产生一个具有更好匹配的函数,那么选择模板。

int Add(int left, int right)
{
	cout << "非模板函数" << endl;
	return left + right;
}
//通用加法模板
template<class T1 , class T2>
T1 Add(T1 left, T2 right)
{
	cout << "模板函数" << endl;
	return left + right;
}
void test()
{
	Add(1, 2);//与非模板函数优先匹配
	Add(1, 2.0);//具备更加匹配的版本而不需要类型转换,编译器优先生成更加匹配的Add函数版本
}
int main()
{
	test();
	return 0;
}

        模板函数不能进行自动类型转换(帮你推演就够忙了,类型转换发生错误你还得骂他自然就不会帮你做这件没意义的事情)。但是普通函数可以进行自动类型转换

类模板

类模板的定义格式

template<class T1, class T2,class T3>//参数列表可以定义多个模板变量
class 类模板名
{
	//类成员定义
};

        我们前面学习顺序表提到过一点,使用typedef对类型名进行重命名。C语言中的类型重命名是指通过使用typedef关键字来为已有的类型创建一个新的别名。这样可以简化代码,提高可读性,并且方便批量修改具体类型,便于维护代码。但是当我们学习了类模板之后我们发现我们并不需要这样做,而是使用类模板。这里我们简要的构造一个动态顺序表的类模板来体会。

template<class T>
class Vector
{
public:
	Vector(size_t capacity = 10)
		: _pData(new T[capacity])
		, _size(0)
		, _capacity(capacity)
	{}

	~Vector();

	// 其他成员函数在这里...

	size_t Size() { return _size; };

	T& operator[](size_t pos)
	{
		assert(pos < _size);
		return _pData[pos];
	}

private:
	T* _pData;
	size_t _size;
	size_t _capacity;
};

// 类模板函数定义可以放在类定义外面。
template<class T>
Vector<T>::~Vector()
{
	if (_pData)
	{
		delete[] _pData;
	}
	_size = _capacity = 0;
}

类模板的实例化

        类模板的实例化与函数模板实例化不同,类模板实例化需要在类模板的名字前面跟<>,然后需要讲实例化的类型放在<>中即可(就像必须显式的实例化)。类模板名字不是真正的类,而实例化的结果才是真正的类(蓝图和用蓝图进行建筑的关系)。实例化时,编译器会生成针对具体数据类型的类定义,从而使得类模板变得具体化,可以像普通类一样使用。

Vector类名,Vector<int>才是类型
Vector<int> s1;
Vector<double> s2;

总结

        泛型编程是一种编程范式,其目标是编写可重用、通用的代码,以便能够适应多种数据类型而无需针对每种类型重复编写相似的代码。在C++中,泛型编程主要通过函数模板和类模板来实现。其优势如下:

  • 提高代码的重用性和可维护性。

  • 允许在不同数据类型上进行抽象,减少代码冗余。

函数模板(Function Templates)

概念:函数模板是一种定义通用函数的方式,其中函数的参数或返回类型可以是通用的类型参数。

语法:

template <class T> //typename也可以
T Add(T a, T b) 
{
    return a + b;
}

实例化:通过指定具体的数据类型,编译器会生成对应类型的函数定义。

实例化用法:

int r_int = Add(3, 4);          // 实例化为 Add<int>(3, 4)
double r_double = Add(3.14, 2.5);// 实例化为 Add<double>(3.14, 2.5)

类模板(Class Templates)

概念:类模板是一种定义通用类的方式,其中类的成员或行为可以依赖于通用的类型参数。

语法:
 

   template <class T> 
   class MyClass 
   {
   public:
       MyClass(T value) : data(value) {}
       void play() { /*  ... */ }
   private:
       T data;
   };

实例化:通过指定具体的数据类型,编译器会生成对应类型的类定义。

使用:

   MyClass<int> intObject(42);
   MyClass<double> doubleObject(3.14);

    作者水平有限,如有错误欢迎指正!


    

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Leetcode—50.Pow(x,n)【中等】

2023每日刷题&#xff08;二十七&#xff09; Leetcode—50.Pow(x,n) 实现代码 double recurPow(double x, long long n) {if(n 1) {return x;}double res recurPow(x, n / 2);if(n % 2 1) {return x * res * res;} else {return res * res;} }double myPow(double x, int…

从0开始python学习-34.pytest常用插件

目录 1. pytest-html&#xff1a;生成HTML测试报告 2.pytest-xdist&#xff1a;并发执行用例 3. pytest-order&#xff1a;自定义用例的执行顺序 4. pytest-rerunfailures&#xff1a;用例失败时自动重试 5. pytest-result-log:用例执行结果记录到日志文件 1. pytest-html…

C进阶-编译环境与预处理

本章重点&#xff1a; 程序的翻译环境 程序的执行环境 详解&#xff1a;C语言程序的编译链接 预定义符号介绍 预处理指令#define 宏和函数的对比 预处理操作符#和##的介绍 命令定义 预处理指令#include 预处理指令#undef 条件编译 1. 程序的翻译环境和执行环境 在ANSI C的任何一…

江西开放大学引领学习新时代:电大搜题助力学子迈向成功

江西开放大学&#xff08;简称江西电大&#xff09;一直以来致力于为学子提供灵活便捷的学习服务。近年来&#xff0c;携手电大搜题微信公众号&#xff0c;江西开放大学以其卓越的教学质量和创新的教学手段&#xff0c;为广大学子开启了一扇通向成功的大门。 作为一家知名的远…

2.如何实现API统一响应-web组件篇

文章目录 1. 统一响应1.1 CommonResult 1. 统一响应 前端调用api接口获得统一的响应&#xff1a; 成功&#xff0c;返回成功的状态码和数据&#xff1b;失败&#xff0c;返回失败的状态码和错误提示。 在标准的 RESTful API 的定义&#xff0c;是推荐使用 HTTP 响应状态码 (…

物联网AI MicroPython学习之语法 network网络配置模块

学物联网&#xff0c;来万物简单IoT物联网&#xff01;&#xff01; network介绍 模块功能&#xff1a; 用于管理Wi-Fi和以太网的网络模块参考用法&#xff1a; import network import time nic network.WLAN(network.STA_IF) nic.active(True) if not nic.isconnected():…

栈和队列:栈

栈的概念&#xff1a; 栈&#xff1a; 一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶&#xff0c;另一端称为栈底。 栈中的数据元素遵守后进先出LIFO&#xff08;Last In First Out&#xff09;的原则。…

如何爬取 python 进行多线程跑数据的内容

下是一个使用 Rust 编写的爬虫程序&#xff0c;用于爬取 python 进行多线程跑数据的内容。这个爬虫使用代理信息进行网络请求&#xff0c;并使用了 Rust 的 async-std 库进行异步编程。 use async_std::net::{TcpStream, TcpListener}; use async_std::io::{BufReader, BufWri…

grpc使用教程

准备 1&#xff0c;安装grpc go get -u google.golang.org/grpc2, 安装go语言protobuf生成器protoc-gen-go go get -u google.golang.org/protobuf/cmd/protoc-gen-go3, 通过下面连接&#xff0c;找到合适版本并安装protoc工具&#xff0c;如windows选择 protoc-3.19.5-win64.…

指标体系:洞察变化的原因

一、指标概述 指标体系是指根据运营目标&#xff0c;整理出可以正确和准确反映业务运营特点的多个指标&#xff0c;并根据指标间的联系形成有机组合。 指标体系业务意义极强&#xff0c;所有指标体系都是为特定的业务经营目的而设计的。指标体系的设计应服从于这种目的&#x…

从0开始python学习-33.夹具@pytest.fixture(scope=““,params=““,autouse=““,ids=““,name=““)

目录 1. 创建夹具 1.1 pytest方式 1.2 unittest方式 2. 使用夹具 2.1 通过参数引用 2.2 通过函数引用 3. 参数详解 3.1 scope&#xff1a;作用域 3.2 params-参数化 3.3 autouseTrue表示自动使用&#xff0c;默认为False 3.4 ids&#xff1a;设置变量名 3.5 name&am…

电脑版微信收到的图片怎么样自动保存到指定文件夹中?

8-5 在平时的工作中&#xff0c;如果你每天都需要接收并保存很多同事发来的图片&#xff0c;如何实现自动保存在微信上接收到的图片呢&#xff1f;本文的方法也许适合你&#xff0c;它可以自动把微信上收到的图片、视频、文件帮你保存到指定地方&#xff0c;可以大大地提高工作…

算法之双指针

双指针算法的作用 双指针算法是一种使用2个变量对线性结构(逻辑线性/物理线性)&#xff0c;进行操作的算法&#xff0c;双指针可以对线性结构进行时间复杂度优化&#xff0c;可以对空间进行记忆。 双指针算法的分类 1.快慢指针 2.滑动窗口 3.左右指针 4.前后指针 双指针OJ题目…

优秀智慧园区案例 - 中建科技产业园(中建·光谷之星),万字长文解析先进智慧园区建设方案经验

一、项目背景 中建科技产业园&#xff08;中建光谷之星&#xff09;&#xff0c;位于武汉光谷中心城、中国&#xff08;湖北&#xff09;自贸试验区武汉片区双核心区&#xff0c;光谷发展主轴高新大道北侧&#xff0c;建筑面积108万平米&#xff0c;是中建三局“中建之星”和“…

基于飞蛾扑火算法优化概率神经网络PNN的分类预测 - 附代码

基于飞蛾扑火算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于飞蛾扑火算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于飞蛾扑火优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神…

深度解剖Linux权限的概念

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大二&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;牢记Linux权限的概念。 > 毒鸡汤&#xff1a;你…

ftp服务器(filezilla服务端软件)下载、安装、使用

下载 通过360软件管家下载 输入filezilla&#xff0c;点击搜索&#xff0c;点击安装 修改安装路径 等待安装完成 配置服务端 启动配置 双击打开&#xff0c;点击软件中间按钮 不用输入密码&#xff0c;因为安装的时候没有设置密码 如果在安装的时候设置了密码&#xff0c;…

Java中的7大设计原则

在面向对象的设计过程中&#xff0c;首先需要考虑的是如何同时提高一个软件系统的可维护性和可复用性。这时&#xff0c;遵从面向对象的设计原则&#xff0c;可以在进行设计方案时减少错误设计的产生&#xff0c;从不同的角度提升一个软件结构的设计水平。 1、单一职责 一个类…

由浅入深学习统计学 - 常用统计图形学习

学习笔记 第一章- 信息图形化 图形化&#xff08;可视化&#xff09; 在一堆数据中&#xff0c;自己发现了这些数据的规律&#xff0c;但是无法表述给其他人知道&#xff0c;图形化就是便于他人理解数据的规律的展示的手段。 或者说我们也可以从统计的数据图形中发现某些没有…

数据结构之带头双向循环链表

前言&#xff1a; 前面我们已经学习了顺序表和单链表&#xff0c;那么我们今天来学习数据结构中的另外一个线性表——带头双向循环链表。 带头双向循环链表&#xff1a; 头结点&#xff1a;带头也就是我们常说的“哨兵位”&#xff0c;头结点其中不存放任何的数据。哨兵位的存在…
最新文章