了解C++模板

153 阅读7分钟

了解C++模板

简介

模板是一种编写单一函数或类的方式,可以被一组类似的函数或类重用。用简单的术语来说,你可以使用模板来声明一个单一的函数或类,并与不同的数据类型一起工作。

本文将介绍模板以及如何使用它们在C++中进行通用编程。

前提条件

要学习这篇文章,你需要具备以下条件。

  • 一个Codeblocks IDE来运行代码。
  • 对C++语言有一个基本的了解。

我们可以用两种不同的方式使用模板的概念。

  1. Class templates
  2. Function templates

模板是如何工作的?

模板在编译时像宏一样展开。不同的是,编译器在扩展模板之前会做一个类型检查。

其原理是基本的,源代码只包含函数/类,但同一函数/类的许多副本可以包含在编译后的代码中。

类模板

类模板是一个使开发者能够操作通用数据类型的模板。这使得类可以按照规范在几种不同的数据类型上使用,而不需要对每种类型进行重新编写。

很多时候,你需要一个类的实现,除了使用的数据类型外,所有的类都是一样的。对于每个数据类型,你必须声明一个单独的类,或者在一个单一的类中建立许多成员变量和函数。

对不同数据类型做同样事情的单独类的声明将使代码库臃肿,并使其难以管理。一个类中的任何变化都需要反映在所有类似的类中。

类模板允许你对所有数据类型重复使用相同的代码。像普通的参数一样,如果需要的话,我们可以将一种以上的数据类型作为参数传输给模板。

类模板的声明

语法。

template <class T>
class class_name
{
   ... .. ...
public:
   T var;
   T functionName(T arg);
   ... .. ...
};

我们来分解一下语法,如下图所示。

  • template <class T>- 它是声明模板的关键字。
  • class - 它是一个用户定义的类型。
  • class_name - 它是类的名称。
  • T - 这是模板的参数,是将要使用的数据类型的占位符。
  • var - 它是通用数据类型的类的一个变量。
  • T functionName(T arg) - 它是通用数据类型的一个函数。

如何创建一个类模板对象?

在开发时,你需要在角括号内定义数据类型< > ,以建立一个类模板对象,如下图所示。

className<dataType> classObject;

比如说。

className<int>classObject;
className<float>classObject;
className<string>classObject;

程序来说明类模板的使用。

#include <iostream>
using namespace std;
// Creation of generic data type
template <class T>

class sample
{
//Declaration of variables of the class
private:
  T digit1, digit2; 
//constructor of the class
public:
  sample(T d1, T d2) //passing the two digits
  {
    digit1 = d1;
    digit2 = d2;
  }
//Function declaration to find the larger number of the two
T larger();
};
template <class T>
//Function definition outside the class
T sample<T>::larger(){
return(digit1>digit2?digit1:digit2); 

}
int main()
{
//Creation of class template objects
  sample <int> big(120, 80);
//Function call to display the larger digit
cout<<"larger= "<<big.larger();
  
  return 0;
}

输出。

larger= 120

在上面的程序中,我们首先声明了模板template<class T> ,并声明了两个变量digit1digit2 。之后,我们创建了构造函数sample() ,并传递了两个参数。

我们还在类T larger() 内创建了函数定义,我们将用它来寻找两个数字中较大的那个。

为了在类外创建T larger() 的函数声明,我们首先声明通用数据类型template <class T> 。请注意,如果你在类外创建的函数声明没有它,编译器会产生错误。

为了在类模板之外建立函数声明,我们首先声明通用返回类型T ,然后声明类名sample 。就在类名之后,我们添加<T> ,以告诉编译器,该函数的模板参数与我们在类中使用的参数相同。

我们在函数定义中比较这两个数字。我们用程序前看到的相同格式创建了一个类对象。

sample <int> big(120, 80);

每次我们在使用类的模板时,都需要告诉它我们要用什么数据类型来替代T 。在这个例子中,我们用T 代替int

之后,我们创建了对象big ,并传递了两个整数。我们通过调用函数T larger() 来完成这个程序。

类模板的优点

使用类模板的主要优点是。

  • 你只需要定义一个类,就可以对各种类型的数据进行操作。
  • 只有那些在应用程序中使用过模板的数据类型在编译时才会产生这个模板类的实例。
  • 正如我们已经确定的,创建和使用模板类将减少开发工作和代码行。它还会减少调试软件的难度和时间,因为你只需要处理一个类。

函数模板

一个函数模板的作用类似于一个普通的函数,但有一个关键区别。一个函数模板可以同时与不同的数据类型交互,但对于一个普通的函数,只有一组数据类型可以工作。

OOP的多态性特征的例子是函数重载和函数模板。如果你需要对两个或更多的数据类型进行类似的操作,你可以使用重载函数来创建两个函数。

当几个函数执行相同的操作时,就会使用模板。使用函数模板将是一种更安全的方法,因为你可以用更少、更可维护的代码完成同样的任务

函数模板的声明?

函数模板以template 关键字开始,伴随着角括号内的参数<> 和函数声明。

语法。

template <class T>
T FunctionName(T arg)
{
   ... .. ...
}

让我们来分解一下语法,如下图所示。

  • template <class T>- 它是声明模板的关键词。
  • T- 它是一个模板参数,接受不同的数据类型。

当一个数据类型的参数被传递给FunctionName() ,编译器就会为给定的数据类型创建一个新版本的FunctionName()

用程序来说明函数模板的使用。

// C++ program to ilustrate use of function templates
#include <iostream>
using namespace std;

// template function
template <class T>
T Larger(T d1, T d2)
{
    return (d1 > d2 ? d1 : d2);
}

int main()
{
    //Declaration of variables with different data types
    int x1, x2;
    float y1, y2;
    char z1, z2;

// When two integer numbers are passed to the function template, the larger number is displayed.

    cout << "Enter two integer numbers:\n";
    cin >> x1 >> x2;
    cout << Larger(x1, x2) <<" is larger." << endl;

// When two floating-point numbers are passed to the function template, the larger number is displayed.

    cout << "\nEnter two floating-point numbers:\n";
    cin >> y1 >> y2;
    cout << Larger(y1, y2) <<" is larger." << endl;

// / The character with a greater ASCII value is shown when two characters are passed to the function template.
    cout << "\nEnter two characters:\n";
    cin >> z1 >> z2;
    cout << Larger(z1, z2) << " has a larger ASCII value.";

    return 0;
}

输出将是。

Enter two integer numbers:
18 10
18 is larger.
Enter two floating-point numbers:
4.5 9.3
9.3 is larger.
Enter two characters:
a m
m has a larger ASCII value.

我们上面的程序需要用户输入,因此对于我们查看输出,我们可以使用以下值。

整数。18, 10 浮点数字。4.5, 9.3 字符:A, M

在上面的程序中声明了函数模板Large() ,它接受两个数据类型为d1d2 的参数TT 表示任何类型都可以作为参数。

两个参数中较大的参数由Large() function 使用条件运算符返回。

三个不同数据类型的变量在main() 函数中被声明。然后,这些变量将作为普通函数传递给Large() 函数原型。

当我们在运行时向函数模板传递一个整数时,编译器知道Large() 函数必须接受int 的参数。

同样,当floatchar 数据被传递时,Large() 函数会被生成,因为它能识别参数的数据类型。这样一来,三个相同的普通函数被使用一个函数原型所取代。

变量函数模板

Variadic函数模板是可以接受多个参数的函数。

由于参数在运行时被解析,所以只能使用参数数量可变的类型安全模板。这是一个比其他模板更好用的模板,因为其他模板只接受有限数量的参数。

语法。

template(typename args)
return_type fun_name(arg variable1, args... variable2)

让我们把语法分解一下,如下图所示。

  • template(typename args)- 它用于声明变体函数模板。
  • return_type- 它是函数的返回类型。
  • fun_name- 它是函数的名称。
  • (arg variable1, args... variables2)- 它是传递给函数的参数。

注意: typename arg,typename... args 必须在角括号内<>

程序来说明variadic函数模板的使用。

// Program to illustrate working of 
// Variadic function Template 

#include <iostream> 
using namespace std; 

// To handle base case of below recursive 
// Variadic function Template 
void display() 
{ 
	cout << "This is an empty function and "
			"it is called last.\n" ; 
} 

// Variadic function template that takes a
// variable number of arguments and displays 
// all. 
template <typename T, typename... Types> 
void display(T variable1, Types... variable2) 
{ 
	cout << variable1 << endl ; 

	display(variable2...) ; 
} 

// Driver code 
int main() 
{ 
	display(1, 2, 3.14, "You can pass me any "
			"number of arguments", 
				"I will display\n"); 

	return 0; 
} 

输出。

1
2
3.14
You can pass me any number of arguments
I will display

This is an empty function and it is called last.

在上面的程序中,变量函数模板的工作方式如下。

display(1, 2, 3.14, “You can pass me any number of arguments”, “I will display\n”); 是以如下方式进行评估的。

编译器首先将该语句解析为。

cout<< 1 <<endl ;
display(2, 3.14, "You can pass me any number of arguments", 
      "I will display\n");

接下来,编译器会寻找一个可以接受这些参数的display() 函数,结果,该函数会以同样的方式再次执行。

cout<< 2 <<endl ;
display(3.14, "You can pass me any number of arguments", 
      "I will display\n");

它再一次被解析成以下形式。

cout<< 3.14 <<endl ;
display("You can pass me any number of arguments", 
      "I will display\n");
cout<< "You can pass me any number of arguments" <<endl ;
display("I will display\n");
cout<< "I will display\n" <<endl ;
display();

编译器在这时寻找一个适合空函数的函数重载,也就是一个不需要参数的函数。

所有带一个或多个参数的函数都被匹配到变量函数中,而空的函数则被匹配到空函数中。

总结

在这篇文章中,我们已经了解了C++中的模板,包括类模板和函数模板,以及如何声明它们。在更广泛的代码库中,我们使用模板来实现代码的重用性和软件的灵活性。