C++模板从入门到精通:初阶篇

奋斗吧
奋斗吧
擅长邻域:未填写

标签: C++模板从入门到精通:初阶篇 C/C++博客 51CTO博客

2023-06-01 18:24:28 47浏览

C++模板从入门到精通:初阶篇,本文章主要涵盖了C++模板的初阶知识,包括泛型编程的概念和实用价值,以及介绍了C++模板的概述,包括模板分类、函数模板和类模板等内容。此外,还针对类模板和模板类之间的区别进行了详细讲解,并且提供了相应的代码实现示例。我们在日常编程中,常常需要编写一些通用的代码,以处理不同类型的数据或对象。此时,泛型编程就发挥了重要作用。它是一种通过使用模板来实现通用代码的编程技术,可以大大提高代码的效率,同时也使得代码更加简洁、易于维护。本文详细介绍了泛型编程的实用价值。接下来,文章介绍了C++模板的概述,包括模板的定义和使用方法,以及函数模板和类模板的区别。在函数模板方面,我们讲解了如何通过函数模板实现通用的函数代码,以及如何在使用函数模板时自动推导模板参数类型。在类模板方面,我们详细介绍了类模板的定义和使用方法,并且针对类模板和模板类之间的区别进行了详细讲解,引导读者正确理解这两个概念之间的区别。最后,文章提供了相应的代码实现示例,以便读者更好地理解泛型编程和C++模板的使用。

一、泛型编程

1.1什么是泛型编程?

平常写的函数与泛型编程的模板实例对比:

实现一个交换的两个数的函数

对于整型

void swap(int& a,int& b)
{
  int temp=a;
  a=b;
  b=temp;
}

对于浮点型

void swap(double& a,double& b)
{
  double temp=a;
  a=b;
  b=temp;
}

对于字符型

void swap(char& a,char& b)
{
  char temp=a;
  a=b;
  b=temp;
}

通过对上面的观察可以发现: 整个函数有很多步骤和思路是重复的。那么有人说使用函数重载可以实现。确实对于这个函数可以用函数重载。但是也有几个不好的点。

  1. 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数
  2. 代码的可维护性比较低,一个出错可能所有的重载均出错


所以引进了模板的概念和泛型编程的方式,目的是让编译器能自动的识别类型利用模板来生成代码并进行运行。可以说这里也极大的减少了重复造轮子。

1.2泛型编程的实用价值

  1. 代码重用:泛型编程可以使得代码更加通用,从而减少了代码的重复。我们可以把那些本质上相同但是类型不同的代码抽象成一个模板,并在不同的地方使用不同的类型来实例化该模板,从而达到代码重用的效果。
  2. 提高代码可读性:泛型编程可以使代码更加通用和简洁,不需要为每种类型都单独编写一份代码。这样,开发人员就可以更专注于算法的实现,而不必过多关注类型的变化,提高了代码的可读性。
  3. 提高代码的灵活性:泛型编程可以根据实际情况选择不同的类型,从而使代码更加灵活。例如,在STL的容器中,只需要指定其中存储的元素的类型,就可以使用其提供的各种方法对该容器进行操作。
  4. 提高代码的性能:在某些情况下,泛型编程可以比具体类型的函数更加高效。例如,对于容器类型来说,使用泛型的迭代器可以将容器中的元素依次遍历,不需要考虑具体的元素类型,从而在性能上有所提升。

二、模板

2.1模板的分类:

C++模板从入门到精通:初阶篇_类模板


模板在编程的时候可以分为:函数模板和类模板。


2.2函数模板

2.2.1函数模板的概念

函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定

类型版本。而不需要为每种类型都定义一个重载函数。

2.2.2函数模板格式

template<typename T1, typename T2,......,typename Tn> //这里typename也可以换成class

返回值类型 函数名(参数列表){}

实例:

#include<iostream>
using namespace std;
template<typename T1>
void  swap1(T1& a, T1& b)
{
	T1 temp = a;
	a = b;
	b = temp;
}
int main()
{
	int a = 10;
	int b = 20;
	swap1(a, b);  // ---->T1 这个模板类型会被自动识别为int
	cout << a <<" "<< b << endl;
	double c = 10.0;
	double d = 20.0;
	swap1(c, d);   // ---->T1 这个模板类型会被自动识别为double
	printf("%lf %lf", c, d);

	return 0;
}

C++模板从入门到精通:初阶篇_函数模板_02

上面代码实现的是两个参数类型都是一致的交换。当传的参数类型不同。

swap1(T1& a,T1& b)
{
  T1 temp=a;
  a=b;
  b=temp;
}
int main()
{
  int a=10;
  double b=20.00;
  swap1(a,b);  //一定会报错
  
  return 0;
}

上面这段代码在调用swap1的时候是会出现歧义,编译器不知道该认为T1是int还是double。

解决方案:

1.模板创建的时候template<typename T1,typename T2>,并且swap函数两个参数分别设成T1& aT2& b

2.在调用函数之前,自己手动强制类型转化。

方案一实例:

#include<iostream>
using namespace std;
template<typename T1, typename T2>
void  swap1(T1& a, T2& b)
{
	T1 temp = b;
	b = a;
	a = temp;
}
int main()
{
	int a = 10;
	double b = 20.00;
	swap1(a, b); 
	cout << a << " " << b << endl;

	return 0;
}

方案二实例:

#include<iostream>
using namespace std;
template<typename T1>
void  swap1(T1& a, T1& b)
{
	T1 temp = a;
	a = b;
	b = temp;
}
int main()
{
	int a = 10;
	double b = 20.00;
	int c = (int)b;
	swap1(a, c);
	cout << a << " " << b << endl;

	return 0;
}




2.2.3函数模板的原理

函数模板的原理就是通过一个图纸或者蓝图,通过识别需求(参数),然后交给编译器的方式产生特具体类型函数的模具。其实模板就是把我们要重复做的事情交给编译器。

特别注意:函数模板本身不是函数

特点:在编译器编译阶段,编译器需要根据传入的实参类型来推演生成对应类型的函数来进行调用。

比如,当T1确定为double或者int,那么编译器会自动生成一份double或者int类型的代码。


C++模板从入门到精通:初阶篇_类模板_03

2.2.4 函数模板的实例化

函数模板的实例化部分在函数模板的格式的时候出现过,函数模板的实例化,就是在调用函数的时候用不同类型的参数使用函数模板。

函数模板的实例化分类:

C++模板从入门到精通:初阶篇_c++_04


1、隐式实例化:就是让编译器去猜,他自己去判断T1是什么类型

#include<iostream>
using namespace std;
template<typename T1>
void  swap1(T1& a, T1& b)
{
	T1 temp = a;
	a = b;
	b = temp;
}
int main()
{
	int a = 10;
	int b = 20;
	swap1(a, b);  // ---->T1 这个模板类型会被自动识别为int
	cout << a << " " << b << endl;
	double c = 10.0;
	double d = 20.0;
	swap1(c, d);   // ---->T1 这个模板类型会被自动识别为double
	printf("%lf %lf", c, d);

	return 0;
}

第一次调用swap1的时候T1会被编译器自动识别为int,第二次自动被识别为double。


2、显式实例化:当两个参数的类型不一致的时候需要我们进行显式实例化

#include<iostream>
using namespace std;

template <typename T>
T add(T a, T b)
{
	return a + b;
}
int main()
{
	int a = 10;
	double b = 20.0;
	cout<<add(a, (int)b)<<endl;   //将double强转为int
	cout<<add((double)a, b)<<endl; //将int强转为double
	cout<<add<int>(a, b)<<endl; //统一将数据类型转化为int
	cout<<add<double>(a, b)<<endl; //统一将数据类型转化为double
	return 0;
}

C++模板从入门到精通:初阶篇_类模板_05



2.2.4模板函数的匹配原则

一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数

template <typename T>
T add(T a, T b)
{
	return a + b;
}
int add(int a, int b)
{
	return a + b;
}

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

模板函数不允许自动类型转换,但普通函数可以进行自动类型转换


2.3类模板

2.3.1类模板的定义格式

template<class T1,class T2>  
class 类模板名
{
  //类成员和函数
};

注意:我们这样写出来的类并不是类,只有实例化后的才是类,这个算是生成具体类的模具。

同时,我们可以在类模板里面嵌套使用函数模板。

2.3.2类模板的实例化

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

2.3.3类模板和模板类的区分

类模板和模板类是同一概念的两种不同说法。它们都指的是可以用于生成不同类型实例的通用代码模板。通常情况下,我们说的是“类模板”,因为这个说法更加准确描述了这种模板的作用和特征。

类模板可以看作是一个带有模板参数的类的定义,其中模板参数可以在类中作为类型、值或其他模板参数使用。而模板类则是通过具体化类模板,即使用特定类型参数替换模板参数而得到的类。模板类本质上就是一个已经实例化的类,可以像普通类一样使用。

因此,类模板和模板类之间的区别在于:类模板是一个通用的模板定义,包含了可以被替换的模板参数;而模板类是通过使用具体类型替换模板参数生成的特定的类实例。


好博客就要一起分享哦!分享海报

此处可发布评论

评论(0展开评论

暂无评论,快来写一下吧

展开评论

您可能感兴趣的博客

客服QQ 1913284695