C++函数模板和类模板超详解

目录

函数模板的使用

函数模板的实例化

模板参数的匹配原则

类模板的使用

类模板的实例化

模板的分离编译


泛型编程可以不针对某种类型的使用,在很多需要重复编写的代码当中能很大程度上减少程序代码量,提高效率

我们先思考一下,如何实现一个通用的交换函数呢?

//可以使用之前的函数重载的知识完成
void Swap(int& left, int& right)
{
  int temp = left;
  left = right;
  right = temp;
}
void Swap(double& left, double& right)
{
  double temp = left;
  left = right;
  right = temp;
}
void Swap(char& left, char& right)
{
 char temp = left;
 left = right;
 right = temp;
}

上面每次增加新的类型交换就要多添加很多程序代码

函数模板的使用

函数模板使用格式:

template<typename T1>//1个模板参数
template<typename T1, typename T2,......,typename Tn>//也可以多个

我们用函数模板再写一下上面的交换函数

//用法
template<typename T>//在交换函数上面写
void Swap(T& left, T& right)
{
  T temp = left;
  left = right;
  right = temp;
}

int main()
{
  int a = 0, b = 1;
  double c = 2.2, d = 3.2;

  Swap(a, b);
  Swap(c, d);
  cout << a << " " << b << endl;//打印 1 0
  cout << c << " " << d << endl;//打印 3.2 2.2 
  return 0;
}

这时我们只需写一个交换函数,就可以实现多种类型的交换,很方便,并且函数模板大部分还会自动推导转换的类型

函数模板的实例化

//用法
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);//隐式实例化 都是int 会自动推导int
  Add(a1, d2);//这时两个类型不同 
  Add<int>((a1, (int)d2);//显示实例化 我们可以指定算的类型
  return 0;
}

模板参数的匹配原则

我们看一个案例熟悉模板参数的匹配原则

//案例:当模板函数和加法函数同时存在
//int的加法函数
int Add(int left, int right)
{
  cout << "调用int加法函数" << endl;
  return left + right;
}

//模板加法函数
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{
  cout << "调用模板加法函数" << endl;
  return left + right;
}

void TestAdd1()
{
  int ret=Add(1, 2);    
  cout << ret << endl;//类型都是int 此时会调用int加法函数
  ret=Add(1, 2.0);
  cout << ret << endl;//此时不全是int,会调用模板加法函数
  ret=Add<int>(1, 2.0);
  cout << ret << endl;//实例化后也会调用模板加法函数   
}
int main()
{
  TestAdd1();
}

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

类模板的使用

先看一个我们完成栈的部分编程代码,此时这个程序只是支持int类型的数据

//栈的实现
class Stack
{
public:
  Stack(int capacity = 0)
  {
	_a = new int[capacity];
	_capacity = capacity;
	_top = 0;
  }
  ~Stack()
  {
	delete[]_a;
	_capacity = 0;
	_top = 0;
  }
  void Push(int x)
  {}

private:
  int* _a;
  int _top;
  int _capacity;

};

int main()
{
  Stack st1;
  st1.Push(1);
  Stack st2;
  st2.Push(2.2);
}

此时想插入double数据,在申请内存、插入数据时都需要修改int,很慢,这时就可以使用类模板

类模板使用格式:

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

我们在用类模板写一下上面的栈实现

template<class T>
class Stack//类模板名
{
public:
 Stack(int capacity = 0)
 {
	_a = new T[capacity];//new capacity个 T类型
	_capacity = capacity;
	_top = 0;
 }
 ~Stack()
 {
	delete[]_a;
	_capacity = 0;
	_top = 0;
 }
 void Push(T x)
 {}

private:
  T* _a;
  int _top;
  int _capacity;

};
int main()
{
  Stack<int> st1;//不能自动推导
  st1.Push(1);//调用就可以传是int的

  Stack<double> st2;
  st2.Push(2.2); //调用就是传double的
}

使用类模板实现栈我们只需要在mian函数中将实例化的(double或int)类型放在<>中即可,省去我们去函数中一个个修改的时间

类模板的实例化

//类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的
//类型放在<>中即可
Vector类名,Vector<int>才是类型
Vector<int> s1;
Vector<double> s2;

注意:类模板名字不是真正的类,而实例化的结果才是真正的类

模板的分离编译

模板分离编译就是分文件编写,最后将所有文件链接起来使用

当我们把加法模板函数声明和定义分别放在.h和.cpp中时运行发现

// Add.h
template<class T>
T Add(const T& left, const T& right);
// Add.cpp
template<class T>
T Add(const T& left, const T& right)
{
  return left + right;
}
 //main.cpp
int main()
{
  Add(1, 2);
  Add(1.0, 2.0);
  return 0;
}

此时把模板函数声明和定义写在两个文件中,就会出现以下链接错误报错

解决方法:我们将声明和定义放到头文件.h里面,将后缀改为 .hpp,就会运行正常

// Add.hpp
template<class T>
T Add(const T& left, const T& right);
template<class T>
T Add(const T& left, const T& right)
{
  return left + right;
}
 
//main.cpp
int main()
{
  Add(1, 2);
  Add(1.0, 2.0);
  return 0;
}

模板函数不支持声明和定义写在两个文件中,会出现链接错误,如果要分离编译的话,可以将声明和定义都放在头文件中,然后修改文件名后缀为.hpp

希望这篇文章大家有所收获,我们下篇见

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值