模板初阶

 泛型编程

函数重载的几个不好的地方:

1.代码的复用率低,新类型出现时,就需要增加对应的函数。例如交换函数的书写时,就能体现出复用率低。

2.代码的可维护性比较低,一个出错可能让所有的重载均出错。

泛型编程:编写与类型无关的通用代码,是代码复用的一种手段,模板是泛型编程的基础。

模板又分为函数模板和类模板。

函数模板 

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

typename是用来定义模板参数关键字,也可以使用class(不能使用struct代替class)

函数模板的原理

模板参数实例化分为:隐式实例化和显式实例化。

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

注意隐式实例化中模板参数列表只有一个T,所以不能有多于一种的参数类型需要去推演。例如int,double两种类型.

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);不能通过编译
  Add(a1,(int)d1);
  Add<int>(a1,d1);
  return 0;
}

 需要注意的点:

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

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

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

类模板

类模板的定义格式 

 

#include<iostream>
#include<cstdio>
#include<assert.h>
#include<algorithm>
using namespace std;
template<class T>
class Vector
{
public:
  Vector(size_t m_capacity=10)
  :pdata(new T[capacity])
  ,size(0)
  ,capacity(m_capacity)
  {}//构造函数初始化
  ~Vector();//使用析构函数在类中声明,在类外定义
  void PushBack(const T& data)
  {
    pdata[size++]=data;
  }
  void PopBack()
  {
    size--;
  }
  size_t Size()
  {
    return size;
  }
  T& operator[](size_t pos)
  {
    assert(pos<size);
    return pdata[pos];
  }
private:
  T* pdata;
  size_t size;
  size_t capacity;
};
//类模板中函数放在类外进行定义时,需要加模板参数列表
template<class T>
Vector<T>::~Vector()
{
  if(pdata)
  {
    delete[] pdata;
  }
}
int main()
{
  Vector<int> s1;
  s1.PushBack(1);
  s1.PushBack(2);
  Vector<double> s2;
  s2.PushBack(1.0);
  s2.PushBack(2.0);
  s2.PushBack(3.0);
  for(size_t i=0;i<s1.Size();i++)
  {
    cout<<s1[i]<<" ";
  }
  cout<<endl;
  for(size_t i=0;i<s2.Size();i++)
  {
    cout<<s2[i]<<" ";
  }
  cout<<endl;
  return 0;
}

模板进阶

非类型模板参数 

 模板参数分类 :类型形参与非类型形参。

类型形参出现在模板参数列表中,跟在class或者typename之类的参数类型名称。

非类型形参,就是用另一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。

****常量的类型只能是:整形(及相关类型),指针,引用。       浮点数类对象以及字符串不允许作为非类型模板参数。

****传入的时候必须是const类型的数据

模板的特化

模板的特化是指在原模板类的基础上,针对特殊类型所进行的特殊化的实现方式。模板特化分为函数模板特化和类模板特化。

函数模板特化步骤:

基础函数模板---->template后面接一对空的尖括号<>   ------------>函数名后跟一对尖括号,尖括号中指定需要特化的类型

------------------->           函数形参表必须要和模板函数的基础参数类型完全相同。

template<>
bool Isequal<char*>(char*& left,char*& right)
{
  if(strcmp(left,right)>0)
  {
    return true;
  }
  return false;
}

类模板特化:

分为全特化和偏特化。

全特化就是将模板参数列表中所有的参数都确定化。

偏特化有以下两种表现方式:

**部分特化   将模板参数类表中的一部分参数特化

**参数更进一步的限制        偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本。

类模板特化应用之类型萃取

1.memcpy,strcpy均是浅拷贝。如果拷贝自定义类型对象,要确定自定义类型对象是浅拷贝还是深拷贝。深拷贝则不能使用memcpy,strcpy

2.使用循环复制的方式代码的效率比较低。 

3.为了将内置类型与自定义类型区分开,可以采用类型萃取。

模板分离编译

经历步骤:

预处理---->编译---->汇编----->链接

模板总结

优点:

  1.  模板复用了代码,节省资源,更快的迭代开发

  2. 增强了代码的灵活性

  3. 进行完封装以后使用起来更加方便,维护性高

缺点:

  1. 导致代码膨胀问题,编译时间也会变长。
  2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误。

更多推荐

C++模板学习