模板的使用

目录

1. 什么是模板?

2. 函数模板

2.1 函数模板的概念

2.2 函数模板的格式

2.3 函数模板的原理

2.4 函数模板的实例化

2.5 模板参数的匹配原理

3. 类模板

3.1 类模板的定义格式

3.2 类模板的实例化

4. 非类型参数模板

5. 模板的特化

5.1 特化的概念

5.2  函数特化

5.3 类模板的特化

5.3.1 全特化

5.3.2 偏特化

 6. 模板的优缺点


1. 什么是模板?

如果我们想实现对不同类型的数据进行交换,首先我们想到的是进行重载,但是重载有几个不好的地方:

  1. 重载的函数只是类型不同,没有使用代码的复用性,并且只要有新的类型出现,就需要添加新的函数
  2. 代码的可维护性差,如果一处出了错误,则都会发生错误

那么有没有比重载更好的方法呢?,我们可不可以告诉编译器一个模板,让它根据不同类型生成不同函数呢?

这就是模板的特点:编写与类型无关的代码,充分利用了代码的复用性

2. 函数模板

2.1 函数模板的概念

函数模板代表了一个函数集体,该模板与类型无关,根据传入的参数类型生成不同的代码

2.2 函数模板的格式

template<typename T1,typename T2,typename T3.... typename Tn>

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

{

     //函数体

}

例如:

这样就实现了简单的函数模板

 2.3 函数模板的原理

什么是函数模板?

实质上函数模板只是一个蓝图,它本身不是函数,是编译器帮助我们将该蓝图生成了一个又一个的代码,将我们要做的事情,交给了编译器去做。

2.4 函数模板的实例化

不同类型的参数使用模板,称为函数模板的实例化,模板参数的实例化又分为:显示实例化和隐式实例化。

1. 隐式实例化

如果我们传入了不同类型的参数,编译器会根据传入的类型推出T的类型,这就是隐式实例化。

但隐式实例化也有些问题,如果传入了两个不同的类型,编译器无法推出T的类型是哪一个,又怎么办呢?

 这时有两个办法:

  1. 将其中传入的一个参数强转成另一个相同的类型

     

     2.显示实例化

2.显示实例化

 在函数后面加上<>,里面写上传入的数据类型,编译器会生成相应类型的代码。

2.5 模板参数的匹配原理

  1. 一个非模板函数和一个同名的模板函数可以共存,同时该模板函数还可以实例化称为该非模板参数

     2. 对于非模板函数和模板函数,如果其他条件相同,则会优先调用非模板函数,而不会调用               模板函数

 为什么会优先调用非模板函数呢?,这是因为会优先调用适合的,并且是已存在的,模板函数还需要进行实例化才能调用

3. 类模板

那既然函数存在通用的问题,那么类模板也存在着上述问题,如果仅仅只是成员的类型不同就需要重新写一份类,那也太麻烦了,更加违背了代码的复用性,因此C++也提供了对类进行模板的设计。

3.1 类模板的定义格式

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

举例:

// 动态顺序表
// 注意:Vector不是具体的类,是编译器根据被实例化的类型生成具体类的模具
template<class T>
class Vector
{
public :
Vector(size_t capacity = 10)
: _pData(new T[capacity])
, _size(0)
, _capacity(capacity)
{}
// 使用析构函数演示:在类中声明,在类外定义。
~Vector();
void PushBack(const T& data);
void PopBack();
// ...
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;
     _size = _capacity = 0;
}

 在类内声明,类外定义时,必须要添加模板参数列表,让编译器识别此处是一个模板,并且在实现函数时,必须要添加上作用域

3.2 类模板的实例化

类模板的实例化必须在类名后加上<>,在<>里面需要添加实例化的类型,类模板名不是真正的类,加上了传入的类型组合起来才是真正的类。

类模板的实例化和函数模板的实例化不同,函数模板的实例化可以隐式实例化,而类模板只能显示实例化,否则编译器将无法对该模板进行操作。

例如上面的类模板:

vector只是类名

只有类名后面加上了<T>才代表类型

比如:

vector<int>、vector<double>

4. 非类型参数模板

参数模板分为:类型参数模板和非类型参数模板

类型参数:跟在class 和 typenam后面的d名称

非类型参数:用一个变量(常量)来作为模板的参数

非类型参数模板比较典型的就是array

我们可以发现array的参数列表 中不仅仅是class,还存在size_t类型

非类型参数模板特点:

  1. 浮点数、类对象和字符串等不能作为非类型参数
  2. 非类型的参数模板在编译期间就已经确定

5. 模板的特化

5.1 特化的概念

什么是特化?

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型可能会造成一些问题,对于这种类型需要特殊处理

比如:

 我们发现当传入地址时,得到的结果并不是我们想要的结果。

我们可以发现当传入的是类型时,结果是正确的,可是当我们传入指针时,结果却和我们所想的不同, 这就属于特殊情况,因此我们需要在这种特殊情况下,对于类模板作出特殊的处理。

这种处理就称为:特化,特化就意味着特殊的处理化,在原模板的基础来,对于传入的特殊类型进行特殊化的实现方式,又因为模板分为函数模板和类模板,因此特化也分为函数特化和类特化。

5.2  函数特化

函数特化的基本步骤:

  1. 必须要有一个基础的模板,供大部分的非特殊类型使用
  2. 关键字template后面需要加上<>
  3. 函数名后跟上一对<>,<>里面写入要特化的类型
  4. 函数的形参必须和原模板的形参相同

根据上述步骤,我们就可以写出针对特定类型的函数了:

这样就完成了对于特殊类型的特殊化处理。

5.3 类模板的特化

 5.3.1 全特化

什么是全特化?

全特化就是模板参数列表中的参数全部确定化

template<class T1, class T2>
class Data
{
public:
	Data()
	{ 
		cout << "Data<T1, T2>" << endl; 
	}
private:
	T1 _d1;
	T2 _d2;
};

//全特化
template<>
class Data<int, char>
{
public:
	Data()
	{ 
		cout << "Data<int, char>" << endl;
	}
private:
	int _d1;
	char _d2;
};
void TestVector()
{
	Data<int, int> d1;
	Data<int, char> d2;
}

int main()
{
	TestVector();
	return 0;
}

 我们发现当传入的类型是特定类型时,就会采用特殊的模板生成的类

5.3.2 偏特化

什么是偏特化?

偏特化指的就是模板参数列表中的一些参数确定化

template<class T1, class T2>
class Data
{
public:
	Data()
	{ 
		cout << "Data<T1, T2>" << endl; 
	}
private:
	T1 _d1;
	T2 _d2;
};

//偏特化
template<class T1>
class Data<T1, char>
{
public:
	Data()
	{ 
		cout << "Data<T1, char>" << endl;
	}
private:
	int _d1;
	char _d2;
};
void TestVector()
{
	Data<int, int> d1;
	Data<double, char> d2;
}

int main()
{
	TestVector();
	return 0;
}

 

 当传入的参数的第二个位置只要是char就会调用偏特化的类模板生成的类。

偏特化有两大应用:

1. 将模板函数列表中的一些参数进行特化

2. 可以对传入参数的进一步限制

//两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:
	Data()
	{ 
		cout << "Data<T1*, T2*>" << endl;
	}
private:
	T1 _d1;
	T2 _d2;
};
//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:
	Data(const T1& d1, const T2& d2)
		: _d1(d1)
		, _d2(d2)
	{
		cout << "Data<T1&, T2&>" << endl;
	}
private:
	const T1& _d1;
	const T2& _d2;
};

 当传入的参数类型是指针或者引用时,会调用特定的类模板生成的类。

6. 模板的优缺点

优点:

1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
2. 增强了代码的灵活性


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


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值