什么是泛型编程与模板

泛型编程

泛型编程简单来说就是编写与类型无关的通用代码,是代码复用的一种手段。而模板是泛型编程的基础
举个简单的例子,假设我们需要写一个swap的交换函数,如果我们按部就班的正常去写的,那么针对以下情况我们就编写出好几种函数,仅仅为了一个简单的交换。

void swap(int& a, int& b);//针对int类型
void swap(double& a, double& b);//针对double类型
void swap(char& a, char& b);//针对char类型

但是,上述函数内部的操作却是相似的,于是为了解决这种问题,我们提出了泛型编程,我们用一个模具来代替类型,让编译器去识别,去替我们做那些重复的事情。
而我们的模具分为了函数模板和类模板,下面就对这两者进行逐一的介绍。

函数模板

概念

函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生特定的函数。也就是使用的时候让编译器来决定本次的类型到底是什么。

格式


template<typename T1, typename T2,......,typename Tn>
返回值类型 函数名(参数列表){}
//以swap函数为例
template<typename T> 
void Swap( T& left,  T& right) {    
	T temp = left;
	left = right;    
	right = temp; 
	}

注意: typename是用来定义模板参数关键字,也可以使用class。

原理

一段代码的执行需要经过 预处理,编译,汇编,链接。这四个阶段。函数模板就是在编译阶段进行替换的,编译器会通过我们出入的参数自动推导出参数类型,在转化成相应的实例化函数,然后进入到下一步汇编,最后链接,执行。
模板函数与普通函数区别就在于编译阶段的实例化生成。

实例化

模板函数的实例化就是通过模板函数生成确定参数的普通函数。
实例化分为以下两种:

  • 隐式实例化 让编译器根据实参推演模板参数的实际类型。
  • 显式实例化:在函数名后的<>中指定模板参数的实际类型

以代码为例:

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

int main() {
   int a1 = 10, a2 = 20;
   double d1 = 10.0, d2 = 20.0;
   Add(a1, a2);    
   Add(d1, d2);
   /*

   Add(a1, d1);

   该语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型
   通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有一个T,
   编译器无法确定此处到底该将T确定为int 或者 double类型而报错    
   注意:在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要背黑锅        
   */       
   // 此时有两种处理方式:1. 用户自己来强制转化 2. 使用显式实例化    
   Add(a1, (int)d1);   
   return 0; 
   }

int main(void) {
	int a = 10; 
	double b = 20.0;  
	// 显式实例化    
	Add<int>(a, b);
	return 0;
}

需要注意的是我们的编译器允许同名的模板函数和非模板函数同时出现,而且编译器还会优先调用非模板函数。两者之间还有一个小区别在于是否可以隐式类型转换,很显然,普通函数可以,模板函数不行。

类模板

定义

template<class T1, class T2, class Tn>
class 类模板名 {    
	// 类内成员定义 
};

实例化

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


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

你可能感兴趣的:(c++基础详解)