首页 > 编程语言 >C++ 模板(函数模板与类模板)

C++ 模板(函数模板与类模板)

时间:2025-01-20 12:54:25浏览次数:1  
标签:cout int C++ template 类型 模板 函数

原文链接:https://www.cnblogs.com/1873cy/p/18398002

模板

模板介绍#

C++提供了函数模板(function template)。所谓函数模板。实际上是建立一个通用函数,其函数类型和形参类型不具体制定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。凡事函数体相同的函数都可以使用这个模板代替,不必定义多个函数,只需在模板中定义一次即可。在调用函数时系统会根据实参的类型来取代模板中的虚拟类型,从而实现不同的函数功能。

C++提供了两种模板机制:函数模板类模板

意义:

  • 模板把函数或类要处理的数据类型参数化,表现为参数的多态性,称为类属。
  • 模板用于表达逻辑结构相同,但具体数据元素类型不同的数据对象的通用行为。

函数模板#

下面举例说明一下函数模板的具体用法。

假设有两个需求:

  • 设计一个函数,实现两个int变量的值的交换
  • 设计一个函数,实现两个double变量的值的交换
void mySwap(int &a, int &b){
	int tmp = a;
	a = b;
	b = tmp;
}
void mySwap(double &a, double &b){
	double tmp = a;
	a = b;
	b = tmp;
}

如果还有需求要交换其他类型的变量,还需要继续添加函数,而代码几乎一致。并且一旦需求有变更,每个函数都要一个一个修改,非常繁琐且重复。

发现两个函数逻辑结构完全相同,只有变量类型不同,如果能设计一个通用的函数,能够把类型当做参数传递到这个函数中,就简单多了——这就是函数模板

定义#

  • 用法:template<typename T>

template:模板关键字
typename:定义虚拟类型关键字,也可以使用class
T:定义的一个虚拟的类型,在这里暂时不确定是什么类型,等到调用这个函数的时候就可以确定了

Copy
  template<typename T>
  void mySwap(T &a, T &b){
  T tmp = a;
  a = b;
  b = tmp;
  }
   
  int main(){
   
  int a = 10, b = 20;
  double x = 3.14, y = 9.9;
   
  // 显式指定类型
  mySwap<int>(a, b);
   
  // 可以自动根据实参的类型进行推导
  mySwap(a, b);
  mySwap(x, y);
   
  cout << "a:" << a << endl;
  cout << "b:" << b << endl;
  cout << "x:" << x << endl;
  cout << "y:" << y << endl;
   
  system("pause");
  return 0;
  }

函数模板使用案例#

  • 需求
    • 定义一个函数模板,实现对数组中元素进行升序排序
    • 定义一个函数模板,实现将一个数组中元素拼接成为字符串返回
Copy
  // 定义一个函数模板,实现对数组中元素进行升序排序
  template<class T>
  void mySort(T arr[], int len){
  for (int i = 0; i < len; i++){
  for (int j = i; j < len; j++){
  if (arr[i] > arr[j]){
  T tmp = arr[i];
  arr[i] = arr[j];
  arr[j] = tmp;
  }
  }
  }
  }
   
  // 定义一个函数模板,实现将一个数组中元素拼接成为字符串返回
  template<class T>
  void showArray(T arr[], int len){
  cout << "[";
  for (int i = 0; i < len - 1; i++){
  cout << arr[i] << ",";
  }
  cout << arr[len - 1] << "]" << endl;
  }
   
  int main(){
  // 定义一个int[]
  int array1[] = { 1, 3, 5, 7, 9, 0, 8, 6, 4, 2 };
  int len1 = sizeof(array1) / sizeof(int);
  mySort(array1, len1);
  showArray(array1, len1);
   
  // 定义一个double[]
  double array2[] = { 3.14, 9.28, 3, 3.44, -9.2, 8.22 };
  int len2 = sizeof(array2) / sizeof(double);
  mySort(array2, len2);
  showArray(array2, len2);
   
  // 定义一个char[]
  char array3[] = { 'a', 'l', '1', 'm', 'k' };
  int len3 = sizeof(array3) / sizeof(char);
  mySort(array3, len3);
  showArray(array3, len3);
   
  system("pause");
  return 0;
  }

函数模板与普通函数#

函数模板和普通函数在调用的时候需要注意:

  • 普通函数的调用是可以发生自动类型转换的;而函数模板调用不允许发生自动类型转换
  • 如果调用函数的时候,实参既可以匹配普通函数,又可以匹配函数模板,则优先匹配普通函数

函数模板重载#

函数模板虽然很通用,但并不是万能的,有时候也会出现不适配的情况

Copy
  template<class T>
  bool compare(const T& t1, const T& t2) {
  return t1 > t2;
  }

对于上述函数模板,如果比较的类型设置为自定义类型(如Person类型),则会出现无法比较的问题。

而函数模板的重载,就是为了解决特定对象的问题,通过函数模板的重载,可以为这些特定的数据类型提供具象化的模板

Copy
  class Person {
  public:
  int age;
  };
   
  template<class T>
  bool compare(const T& t1, const T& t2) {
  return t1 > t2;
  }
   
  template<>
  bool compare<Person>(const Person& p1, const Person& p2) {
  return p1.age > p2.age;
  }
   
   
  int main() {
   
  Person p1;
  p1.age = 15;
   
  Person p2;
  p2.age = 12;
   
  cout << compare(p1, p2) << endl;
   
   
  return 0;
  }

类模板#

类模板和函数模板的定义和使用基本是一样的,但是类模板和函数模板还是有点区别:

  • 类模板不能自动类型推导
Copy
  template<class T1, class T2 = int> // 可以使用 = 指定默认类型
  class NumberOperator {
  public:
  T1 num1;
  T2 num2;
   
  void cal() {
  cout << num1 + num2 << endl;
  }
  };
   
  int main() {
   
  // 创建对象,不能类型推导,只能自己指定类型
  NumberOperator<int, int> op1;
  op1.num1 = 10;
  op1.num2 = 20;
  op1.cal();
   
  // 创建对象
  NumberOperator<double> op2; // 默认 T2 = int
  op2.num1 = 3.14;
  op2.num2 = 10;
  op2.cal();
   
  return 0;
  }

类模板做函数参数#

Copy
  template<class T1, class T2 = int>
  class NumberOperation {
  public:
  T1 num1;
  T2 num2;
   
  void cal() {
  cout << num1 + num2 << endl;
  }
  };
   
  // 参数中明确模板类
  void useNumberOperation(NumberOperation<int, int>& op) {
  op.cal();
  }
   
  // 参数中使用模板
  template<typename T1, typename T2>
  void useNumberOperation02(NumberOperation<T1, T2>& op) {
  op.cal();
  }
   
  int main() {
   
  // 参数明确模板类调用
  NumberOperation<int, int> op;
  op.num1 = 10;
  op.num2 = 20;
  useNumberOperation(op);
   
  // 参数模板
  NumberOperation<double, int> op2;
  op2.num1 = 10.5;
  op2.num2 = 5;
  useNumberOperation02(op2);
   
  return 0;
  }

类模板继承#

Copy
  // 定义模板类
  template<typename T>
  class Animal {
  public:
  T arg;
  };
   
  // 普通类继承模板类的时候,必须明确指定类型
  class Dog: Animal<int> {
  // 这里继承到的arg的数据类型是int
  };
   
  template<typename E>
  class Person: Animal<E> {
  // 这里继承到的arg的数据类型是E
  };

类模板类外实现#

Copy
  template<typename T, typename M>
  class NumberCalculator {
  private:
  T n1;
  M n2;
  public:
  NumberCalculator() {}
  NumberCalculator(T n1, M n2);
   
  void add();
  };
  // 构造函数类外实现
  template<typename T, typename M>
  NumberCalculator<T, M>::NumberCalculator(T n1, M n2) {
  this->n1 = n1;
  this->n2 = n2;
  }
  // 普通函数类外实现
  template<typename T, typename M>
  NumberCalculator<T, M>::add() {
  cout << n1 + n2 << endl;
  }

类模板头文件和源文件分离问题#

我们在写程序时,很多时候需要将类的声明和实现分开来写。将类的声明部分写到.h文件中,将类的实现部分写到.cpp文件中。在使用到这个类的时候,直接包含.h文件即可。但当一个类是模板类时,这样做会出现问题。

我们虽然引入了.h文件,但是模板类中的函数是在调用的时候才会创建,因此在编译阶段不会管对应的.cpp文件中的实现部分。
而到了使用这个函数的时候,发现这个函数已经创建了但是没有实现,编译器也会因此而报错。相当于我们只是在.h中声明了函数,但是并没有实现。

解决办法:

  • 使用#include引入.cpp文件
  • 或是将类的声明和实现放到一个文件中(这个文件我们习惯上会定义为.hpp文件,但并不是绝对的,只是一个习惯和约定的问题。)

模板

模板介绍#

C++提供了函数模板(function template)。所谓函数模板。实际上是建立一个通用函数,其函数类型和形参类型不具体制定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。凡事函数体相同的函数都可以使用这个模板代替,不必定义多个函数,只需在模板中定义一次即可。在调用函数时系统会根据实参的类型来取代模板中的虚拟类型,从而实现不同的函数功能。

C++提供了两种模板机制:函数模板类模板

意义:

  • 模板把函数或类要处理的数据类型参数化,表现为参数的多态性,称为类属。
  • 模板用于表达逻辑结构相同,但具体数据元素类型不同的数据对象的通用行为。

函数模板#

下面举例说明一下函数模板的具体用法。

假设有两个需求:

  • 设计一个函数,实现两个int变量的值的交换
  • 设计一个函数,实现两个double变量的值的交换
Copy
  void mySwap(int &a, int &b){
  int tmp = a;
  a = b;
  b = tmp;
  }
  void mySwap(double &a, double &b){
  double tmp = a;
  a = b;
  b = tmp;
  }

如果还有需求要交换其他类型的变量,还需要继续添加函数,而代码几乎一致。并且一旦需求有变更,每个函数都要一个一个修改,非常繁琐且重复。

发现两个函数逻辑结构完全相同,只有变量类型不同,如果能设计一个通用的函数,能够把类型当做参数传递到这个函数中,就简单多了——这就是函数模板

定义#

  • 用法:template<typename T>

template:模板关键字
typename:定义虚拟类型关键字,也可以使用class
T:定义的一个虚拟的类型,在这里暂时不确定是什么类型,等到调用这个函数的时候就可以确定了

Copy
  template<typename T>
  void mySwap(T &a, T &b){
  T tmp = a;
  a = b;
  b = tmp;
  }
   
  int main(){
   
  int a = 10, b = 20;
  double x = 3.14, y = 9.9;
   
  // 显式指定类型
  mySwap<int>(a, b);
   
  // 可以自动根据实参的类型进行推导
  mySwap(a, b);
  mySwap(x, y);
   
  cout << "a:" << a << endl;
  cout << "b:" << b << endl;
  cout << "x:" << x << endl;
  cout << "y:" << y << endl;
   
  system("pause");
  return 0;
  }

函数模板使用案例#

  • 需求
    • 定义一个函数模板,实现对数组中元素进行升序排序
    • 定义一个函数模板,实现将一个数组中元素拼接成为字符串返回
Copy
  // 定义一个函数模板,实现对数组中元素进行升序排序
  template<class T>
  void mySort(T arr[], int len){
  for (int i = 0; i < len; i++){
  for (int j = i; j < len; j++){
  if (arr[i] > arr[j]){
  T tmp = arr[i];
  arr[i] = arr[j];
  arr[j] = tmp;
  }
  }
  }
  }
   
  // 定义一个函数模板,实现将一个数组中元素拼接成为字符串返回
  template<class T>
  void showArray(T arr[], int len){
  cout << "[";
  for (int i = 0; i < len - 1; i++){
  cout << arr[i] << ",";
  }
  cout << arr[len - 1] << "]" << endl;
  }
   
  int main(){
  // 定义一个int[]
  int array1[] = { 1, 3, 5, 7, 9, 0, 8, 6, 4, 2 };
  int len1 = sizeof(array1) / sizeof(int);
  mySort(array1, len1);
  showArray(array1, len1);
   
  // 定义一个double[]
  double array2[] = { 3.14, 9.28, 3, 3.44, -9.2, 8.22 };
  int len2 = sizeof(array2) / sizeof(double);
  mySort(array2, len2);
  showArray(array2, len2);
   
  // 定义一个char[]
  char array3[] = { 'a', 'l', '1', 'm', 'k' };
  int len3 = sizeof(array3) / sizeof(char);
  mySort(array3, len3);
  showArray(array3, len3);
   
  system("pause");
  return 0;
  }

函数模板与普通函数#

函数模板和普通函数在调用的时候需要注意:

  • 普通函数的调用是可以发生自动类型转换的;而函数模板调用不允许发生自动类型转换
  • 如果调用函数的时候,实参既可以匹配普通函数,又可以匹配函数模板,则优先匹配普通函数

函数模板重载#

函数模板虽然很通用,但并不是万能的,有时候也会出现不适配的情况

Copy
  template<class T>
  bool compare(const T& t1, const T& t2) {
  return t1 > t2;
  }

对于上述函数模板,如果比较的类型设置为自定义类型(如Person类型),则会出现无法比较的问题。

而函数模板的重载,就是为了解决特定对象的问题,通过函数模板的重载,可以为这些特定的数据类型提供具象化的模板

Copy
  class Person {
  public:
  int age;
  };
   
  template<class T>
  bool compare(const T& t1, const T& t2) {
  return t1 > t2;
  }
   
  template<>
  bool compare<Person>(const Person& p1, const Person& p2) {
  return p1.age > p2.age;
  }
   
   
  int main() {
   
  Person p1;
  p1.age = 15;
   
  Person p2;
  p2.age = 12;
   
  cout << compare(p1, p2) << endl;
   
   
  return 0;
  }

类模板#

类模板和函数模板的定义和使用基本是一样的,但是类模板和函数模板还是有点区别:

  • 类模板不能自动类型推导
Copy
  template<class T1, class T2 = int> // 可以使用 = 指定默认类型
  class NumberOperator {
  public:
  T1 num1;
  T2 num2;
   
  void cal() {
  cout << num1 + num2 << endl;
  }
  };
   
  int main() {
   
  // 创建对象,不能类型推导,只能自己指定类型
  NumberOperator<int, int> op1;
  op1.num1 = 10;
  op1.num2 = 20;
  op1.cal();
   
  // 创建对象
  NumberOperator<double> op2; // 默认 T2 = int
  op2.num1 = 3.14;
  op2.num2 = 10;
  op2.cal();
   
  return 0;
  }

类模板做函数参数#

Copy
  template<class T1, class T2 = int>
  class NumberOperation {
  public:
  T1 num1;
  T2 num2;
   
  void cal() {
  cout << num1 + num2 << endl;
  }
  };
   
  // 参数中明确模板类
  void useNumberOperation(NumberOperation<int, int>& op) {
  op.cal();
  }
   
  // 参数中使用模板
  template<typename T1, typename T2>
  void useNumberOperation02(NumberOperation<T1, T2>& op) {
  op.cal();
  }
   
  int main() {
   
  // 参数明确模板类调用
  NumberOperation<int, int> op;
  op.num1 = 10;
  op.num2 = 20;
  useNumberOperation(op);
   
  // 参数模板
  NumberOperation<double, int> op2;
  op2.num1 = 10.5;
  op2.num2 = 5;
  useNumberOperation02(op2);
   
  return 0;
  }

类模板继承#

Copy
  // 定义模板类
  template<typename T>
  class Animal {
  public:
  T arg;
  };
   
  // 普通类继承模板类的时候,必须明确指定类型
  class Dog: Animal<int> {
  // 这里继承到的arg的数据类型是int
  };
   
  template<typename E>
  class Person: Animal<E> {
  // 这里继承到的arg的数据类型是E
  };

类模板类外实现#

Copy
  template<typename T, typename M>
  class NumberCalculator {
  private:
  T n1;
  M n2;
  public:
  NumberCalculator() {}
  NumberCalculator(T n1, M n2);
   
  void add();
  };
  // 构造函数类外实现
  template<typename T, typename M>
  NumberCalculator<T, M>::NumberCalculator(T n1, M n2) {
  this->n1 = n1;
  this->n2 = n2;
  }
  // 普通函数类外实现
  template<typename T, typename M>
  NumberCalculator<T, M>::add() {
  cout << n1 + n2 << endl;
  }

类模板头文件和源文件分离问题#

我们在写程序时,很多时候需要将类的声明和实现分开来写。将类的声明部分写到.h文件中,将类的实现部分写到.cpp文件中。在使用到这个类的时候,直接包含.h文件即可。但当一个类是模板类时,这样做会出现问题。

我们虽然引入了.h文件,但是模板类中的函数是在调用的时候才会创建,因此在编译阶段不会管对应的.cpp文件中的实现部分。
而到了使用这个函数的时候,发现这个函数已经创建了但是没有实现,编译器也会因此而报错。相当于我们只是在.h中声明了函数,但是并没有实现。

解决办法:

  • 使用#include引入.cpp文件
  • 或是将类的声明和实现放到一个文件中(这个文件我们习惯上会定义为.hpp文件,但并不是绝对的,只是一个习惯和约定的问题。)

标签:cout,int,C++,template,类型,模板,函数
From: https://www.cnblogs.com/jijm123/p/18681138

相关文章

  • 深入Node.js工具函数:前端开发的得力助手
    文章目录引言1.Node.js工具函数基础1.1常用工具函数概述1.2工具函数与前端开发的关联2.核心工具函数解析2.1path模块2.1.1resolve函数2.1.2join函数2.2fs模块2.2.1readFile与writeFile2.2.2mkdir与rmdir2.3util模块2.3.1inherits函数2.3.2inspe......
  • 如何修改网站模板结构:详细指南
    修改网站模板结构是提升网站设计和用户体验的重要步骤。以下是详细的修改步骤和注意事项,适用于各种类型的网站管理系统:备份网站文件:在进行任何文件修改之前,务必备份网站的所有文件和数据库。您可以使用FTP工具下载网站文件,或者通过网站托管商提供的备份功能进行备份。确保备......
  • 如何修改网站模板:详细教程
    修改网站模板是提升网站设计和用户体验的重要步骤。以下是详细的修改步骤和注意事项,适用于各种类型的网站管理系统:备份网站文件:在进行任何文件修改之前,务必备份网站的所有文件和数据库。您可以使用FTP工具下载网站文件,或者通过网站托管商提供的备份功能进行备份。确保备份文......
  • 百度网站模板修改的步骤与注意事项
    如果您需要修改百度网站的模板,确保网站的外观和功能符合预期,可以遵循以下步骤和注意事项:备份现有模板:在进行任何更改之前,请确保对当前使用的模板进行完整备份。这可以防止意外错误导致网站无法正常运行。获取访问权限:确保您有足够的权限来编辑模板文件。如果您不是管理员,则需要......
  • 如何修改织梦(DedeCMS)网站地图生成模板以优化SEO
    修改织梦(DedeCMS)的网站地图生成模板是优化网站SEO的重要步骤。以下是详细的指南,帮助您顺利完成这一任务:备份现有模板:在进行任何更改之前,请确保对当前使用的网站地图模板进行完整备份。这可以防止意外错误导致网站地图无法正常生成。登录织梦后台管理系统:进入织梦网站的......
  • 你所不知道的 C/C++ 宏知识——基于《C/C++ 宏编程的艺术》
    前言刚学C++的时候,就知道它糅合了四种编程模式:基于预处理器的宏、基于C语言的面向过程、基于类的面向对象、以及基于模板的泛型编程。其中,宏和模板元编程因为是在编译期出结果,能有效提升程序运行期性能,有着独特的价值。宏的缺陷之前了解的宏编程,大多数在数说它的缺陷,以及如......
  • [oeasy]python062_提示符是怎么来的_[词根溯源]prompt_input_输入函数_提示符
    提示符是怎么来的_[词根溯源]prompt_input_输入函数_提示符回忆上次内容上次讲的是从键盘输入变量的值 input函数可以接收到输入字符串存在变量里   添加图片注释,不超过140字(可选) input函数的参数叫prompt......
  • 50个C++经典面试题(01~10)
    为什么要研究面试题,因为研究题目可以让面试者面试时看起来像个专家。本博文将给出50个面试题,题目涉及初级、中级、高级。下面来看下01~10题01:C++是什么?C++的优势是什么?C++是一门面向对象语言,它的存在的目的是为了克服C语言的短板。其中面向对象又涉及如下几个概念:polym......
  • 矩阵乘法与加法模板 Matrix
    更新日志20250119:开工。运算对于矩阵乘法运算\[A\timesB=C\]你可以认为,\(C\)中第\(i\)行第\(j\)列的元素,就是\(A\)的第\(i\)行依次与\(B\)的第\(j\)列相乘的乘积之和。形式化的:\[C_{i,j}=\sum_{k=1}^nA_{i,k}B_{k,j}\]写成代码就是:rep(i,1,n)rep(j,1......
  • C++auto和decltype的用法
    在C++中,auto和decltype是两个非常有用的关键字,它们帮助程序员更方便地处理类型推导和类型声明。以下是它们的具体用法:autoauto关键字用于自动类型推导,即让编译器根据初始化表达式来推断变量的类型。这在处理复杂类型或模板编程时特别有用,因为它可以简化代码并减少类型错误。......