C++系列-泛型编程概念及函数模板

devtools/2024/10/20 21:29:11/

泛型编程概念及函数模板

  • 泛型编程的引入
  • 模板
    • 函数模板
    • 函数模板注意事项
    • 函数模板举例
    • 普通函数和函数模板的区别
      • 隐式类型转换上的区别
      • 调用上的规则和区别
    • 模板的局限性
    • 学习模板的意义


白云泉
唐代:白居易
天平山上白云泉,云自无心水自闲。
何必奔冲山下去,更添波浪向人间。


泛型编程的引入

想要比较两个整形数据的大小,写一个形参数是整形的函数。想要比较两个浮点型数据的大小,写一个形参是浮点型的函数。想要比较两个字符的ascii码的大小,写一个形参是char的函数。实际上几个函数的代码都差不多,那你觉得啰里八嗦写了一堆代码,烦不烦?
所以,如果可以把数据类型也作为一种可以灵活传递的参数,就太人性了。这种编程就是泛型编程了。

泛型,广泛的类型,指的仍是一种数据类型,它是一种通用类型,它可以代替其它多种类型,这个方法可以大规模的减少程序代码的编写量和重复性。泛型编程利用的主要技术是模板。

模板

C++中有两种模板机制:函数模板,类模板

函数模板

  • 函数的返回类型和形参的类型可以不具体指定,用代号T来表示,T称为泛型(generic type)。使用这种类型占位符的编程方式就叫泛型编程。
  • 在函数调用的时候再去确认T的实际类型。
  • 语法: template, 在有了这个声明后,其下紧跟的函数就是一个函数模板。template:声明创建模板,typename:表明其后的符号代表一种数据类型,也可以用class代替。后面可以有多个typename。T:通用数据类型,也可以用其它符号代替。
  • 函数模板的两种实现方式
    – 自动类型转换
    – 显式指定类型,推荐。
code:
# include <iostream>
using namespace std;template <typename T>	// 声明一个模板,告诉编译器后面代码中紧跟着的T是一个通用数据模型	
T max_2(T a, T b)		// T是抽象的一个类型,在使用的时候再确定具体类型
{T result = a > b ? a : b;cout << "the max value: " << result << endl;return result;
}template <typename T>	// 声明一个模板,告诉编译器后面代码中紧跟着的T是一个通用数据模型
void swap_2(T &a, T &b)	// T是抽象的一个类型,在使用的时候再确定具体类型
{T temp = a;a = b;b = temp;
}int main()
{max_2(8, 5);max_2(8.6, 9.5);int a = 666;int	b = 888;cout << "原始的a: " << a << ", 原始的b: " << b << endl;swap_2(a, b);		// 模板实现的方式是自动类型转换cout << "交换后的a: " << a << ", 交换后的b: " << b << endl;float c = 666.666;float d = 888.888;cout << "原始的c: " << c << ", 原始的d: " << d << endl;swap_2<float>(c, d);		// 模板实现的方式是显式指定类型cout << "交换后的c: " << c << ", 交换后的d: " << d << endl;system("pause");return 0;
}result:
the max value: 8
the max value: 9.5
原始的a: 666, 原始的b: 888
交换后的a: 666, 交换后的b: 888
原始的c: 666.666, 原始的d: 888.888
交换后的c: 888.888, 交换后的d: 666.666

函数模板注意事项

  • 如果使用自动类型推导,如果只使用类型T,则必须要能推导出一致的数据类型T。
  • T必须要有确定的类型,可以使用显示指示类型方式直接指定。
code:
#include <iostream>
using namespace std;template <typename T>
void swap_1(T& a, T& b)	// T是抽象的一个类型,在使用的时候再确定具体类型
{T temp = a;a = b;b = temp;
}template <typename T>
void print_info()
{cout << "print_info函数" << endl;
}int main()
{int a = 11;	int b = 22;char c = 'g';//swap_1(a, c);			// 错误,在模板中类型T,函数调用是有int,有char,无法推导出T的类型cout << "原始的a: " << a << ", 原始的b: " << b << endl;swap_1(a, b);			// 模板实现的方式是自动类型转换cout << "交换后的a: " << a << ", 交换后的b: " << b << endl;// print_info();		// 错误,没有确定的T,无法使用自动类型推导的方式print_info<int>();		// 使用显示指定类型,T有了确定的类型system("pause");return 0;
}result:
原始的a: 11, 原始的b: 22
交换后的a: 22, 交换后的b: 11
print_info函数

函数模板举例

code:
#include <iostream>
using namespace std;
template <typename T>
void my_swap(T &a, T &b)
{T temp = a;a = b;b = temp;
}template <typename T>
void my_sort(T array[], int num)	// T是抽象的一个类型,在使用的时候再确定具体类型
{for(int i_loop=0; i_loop<num; i_loop++){int max_idx = i_loop;for (int j_loop = i_loop + 1; j_loop < num; j_loop++){if (array[j_loop] > array[max_idx]){my_swap(array[j_loop], array[max_idx]);}}}
}template <typename T>
void print_info(T array[], int num)
{for (int i_loop = 0; i_loop < num; i_loop++){cout << array[i_loop] << " ";}cout << endl;
}template <typename T>
void test01(T array, int num)		// 这里的T是个指针类型的
{cout << "排序前的数据: ";print_info(array, num);my_sort(array, num);cout << "排序后的数据: ";print_info(array, num);
}int main()
{int array1[] = { 1,0,-2,4,8,10,6,9,44 };int num1 = sizeof(array1) / sizeof(array1[0]);char array2[] = "afgbdozs";int num2 = sizeof(array1) / sizeof(array1[0]);test01(array1, num1);test01<char*>(array2, num2);system("pause");return 0;
}result:
排序前的数据: 1 0 -2 4 8 10 6 9 44
排序后的数据: 44 10 9 8 6 4 1 0 -2
排序前的数据: a f g b d o z s
排序后的数据: z s o g f d b a

普通函数和函数模板的区别

隐式类型转换上的区别

  • 普通函数调用时可以发生自动类型转换(隐式类型转换)。
  • 函数模板调用时,如果使用自动类型推导,不发生隐式类型转换。
  • 函数模板调用时,如果使用显式指定类型的方式,可以发生隐式类型转换。
code:
#include <iostream>
using namespace std;
template <typename T>
void my_swap(T& a, T& b)
{T temp = a;a = b;b = temp;
}void my_add01(int a, int b)
{	cout << "add result: " << a + b << endl;
}template <typename T>
void my_add02(T a, T b)	// T是抽象的一个类型,在使用的时候再确定具体类型
{cout << "add result: " << a + b << endl;
}int main()
{int a = 68;int b = 22;char c = 'a';cout << "普通函数调用" << endl;my_add01(a, b);my_add01(a, c);cout << "函数模板自动类型推导调用" << endl;my_add02(a, b);// my_add02(a, c);		// 错误,无法实现隐式类型转换,不知道替换成是int还是charcout << "函数模板显式指定类型调用" << endl;my_add02<int>(a, b);my_add02<int>(a, c);system("pause");return 0;
}result:
普通函数调用
add result: 90
add result: 165
函数模板自动类型推导调用
add result: 90
函数模板显式指定类型调用
add result: 90
add result: 165

调用上的规则和区别

既然有函数模板,就不要再提供普通函数了,不明确。实际开发中最好不要同时出现二者

  • 如果普通函数和函数模板都可以调用,优先普通函数。
  • 可以使用显式指定类型的方式明确调用函数模板,也可以在类型指定中指定为空,强制调用。
  • 函数模板也可以实现重载。
  • 如果函数模板产生更好的匹配,则优先调用函数模板。
code:
// 如果普通函数和函数模板都可以调用,优先普通函数。
#include <iostream>
using namespace std;void print_info(int a)
{cout << "调用的是普通函数" << endl;
}template<typename T>
void print_info(T a)
{cout << "T的类型" << typeid(T).name() << endl;
}template<typename T1, typename T2>
void print_info(T1 a, T2 b)
{cout << "T1的类型" << typeid(T1).name() << endl;cout << "T2的类型" << typeid(T2).name() << endl;
}int main()
{cout << "---------- 1 -----------" << endl;print_info(3);			// 默认是调用普通函数的cout << "---------- 2 -----------" << endl;print_info<>(3);		// 通过空模板参数列表实现函数模板的调用,也可以显式指定类型法cout << "---------- 3 -----------" << endl;print_info<int, char>(33, 'a');		// 函数模板也可以发生重载cout << "---------- 4 -----------" << endl;print_info<int>(66);				// 函数模板也可以发生重载cout << "---------- 5 -----------" << endl;// 如果使用函数模板,则会直接将T指定为char,而如果使用普通函数,则需要进行隐式类型转换// 函数模板产生更好的匹配,则优先调用函数模板system("pause");print_info('c');		return 0;
}result:
---------- 1 -----------
调用的是普通函数
---------- 2 -----------
T的类型int
---------- 3 -----------
T1的类型int
T2的类型char
---------- 4 -----------
T的类型int
---------- 5 -----------

模板的局限性

  • 模板并非是万能的,有些特定的数据类型,需要做可特殊的实现。
    先看如下代码:
    如果func1中传的参数是数组,则在调用阶段会出错。
    如果func2中传的参数是类的对象,按照什么比较大小?
template<typename T>
void func1(T a, T b)
{a = b;
}template<typename T>
T func2(T a, T b)
{return a > b ? a : b;
}

利用具体化的模板,可以解决自定义类型的通用化

  • 下面代码中,在my_compare函数中,如果传递的参数是Person类型的,是无法比较大小的,所以单独对Person类型的实现具体化的代码,则当传递的参数是Person类型时,会走具体化的代码。
  • template<> bool my_compare(Person a, Person b), 前面要有template<> ,函数定义中的参数类型直接指明为Person。
code:
#include <iostream>
using namespace std;class Person
{
public:Person(string name, int age){m_name = name;m_age = age;}string m_name;int m_age;
};template<typename T>
bool my_compare(T a, T b)
{cout << "T的类型: " << typeid(T).name() << endl;//return (a == b) ? true : false;		// 如果T是Person,这里的比较会报错,无法用==比较,除非==重载return true;
}// 利用具体化的Person的代码实现,具体化调用,当传递T是Person时,走这段代码
template<>
bool my_compare(Person a, Person b)
{if ((a.m_name == b.m_name) && (a.m_age == b.m_age)){return true;}else{return false;}
}void test01()
{Person p1("Jerry", 2);Person p2("Tom", 5);cout << (my_compare(p1, p2) ? "a==b" : "a!=b") << endl;my_compare(3, 5);
}
int main()
{test01();system("pause");return 0;
}result:
a!=b
T的类型: int

学习模板的意义

  • 一般不是为了写模板,而是在STL能够运用系统提供的模板。

注意:本文部分内容来自黑马


http://www.ppmy.cn/devtools/103202.html

相关文章

昇思AI框架实践1:安装MindSpoe和MindFormers

mindspore的python环境安装 项目需要安装MindSpoe和MindFormers两个软件包&#xff0c;最简单的安装方法是pip install安装&#xff1a; pip install mindspore2.2.0 mindformers-1.0.0 下面是详细的安装过程。 下载安装miniconda&#xff08;python环境&#xff09; mini…

MariaDB VS MySQL

MariaDB和MySQL是两种流行的开源关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;它们在功能、性能、兼容性、开源性以及社区支持等方面各有特点。以下是对两者主要区别的详细分析&#xff1a; 1. 开发者与起源 MySQL&#xff1a;自1995年问世以来&#xff0c…

CI/CD之Jenkins用于Linux系统的部署方式汇总

目录 一、前言 二、CI/CD的定义与核心原则 CI/CD在现代软件开发中的重要性 CI/CD与Jenkins的关系 三、Jenkins部署方式汇总 1. 独立服务器部署 &#xff08;1&#xff09;离线安装 &#xff08;2&#xff09;在线安装 2. Docker容器部署 3. Kubernetes集群部署 4. 云…

CSS3实现购物车动画效果

概述 小程序商城或者 web 端网站时,我们可以只通过 CSS 的 animation 和transform,而不需要借助额外的第三方库轻松实现简单的动画效果,丰富页面的表达效果 效果 如下图所示,点击按钮就会有个商品进入左下角的购物车内 购物车动画示例地址 代码示例 元素 开始只需要写…

RAG数据集自动构造探索, 附prompt

从文档中手动创建数百个 QA&#xff08;问题-上下文-答案&#xff09;样本可能非常耗时且劳动密集。此外&#xff0c;人工生成的问题可能难以达到全面评估所需的复杂程度&#xff0c;最终影响评估的质量。通过使用合成数据生成&#xff0c;开发人员在数据聚合过程中的时间可以减…

绕过后端校验的策略

绕过后端校验的策略 在软件开发中&#xff0c;后端校验是确保数据的正确性和安全性的重要措施之一。但是在某些情况下&#xff0c;我们需要绕过后端校验&#xff0c;以实现特殊的需求或功能。以下是一些常见的绕过后端校验的策略&#xff0c;以及它们的实例解释。 1. 使用代理…

Redis下载安装使用教程图文教程(超详细)

《网络安全自学教程》 Redis下载安装使用 1、下载安装2、基础配置2.1、远程连接2、保护模式3、默认密码4、后台运行4.1 验证服务是否启动4.2 停止服务 1、下载安装 1&#xff09;到Redis中文网 https://www.redis.com.cn/download.html 下载Redis安装包 2&#xff09;Redis的安…

一个多端阅读、功能完善的原创小说 CMS 系统,支持 PC 和 WAP 多端阅读平台、自动化爬虫、定制化模板、作家管理(附源码)

前言 在数字阅读时代&#xff0c;网络小说成为了许多人的日常消遣。然而&#xff0c;现有的小说阅读平台往往存在着用户体验不佳、功能单一等问题。为了改善这一现状&#xff0c;一款名为 Novel Plus 的原创文学 CMS 系统应运而生。 本文将详细介绍 Novel Plus 的功能特点、技…