C++模板初阶

专栏:C/C++
个人主页:HaiFan.
专栏简介:本章为大家带来C++的模板(初阶)。

前言

面向对象编程(OOP)和泛型编程都能处理在编写程序时不知道类型的情况。不同之处在于:OOP能处理类型在程序运行之前都未知的情况;而在泛型编程中,在编译就能获知类型了。

模板是泛型编程的基础。一个模板就是一个创建类或者函数的蓝图或者说是公式。当使用一个vector这样的泛型类型,或者find这样的泛型函数时,我们提供足够的信息,将蓝图转换为特定的类或者函数。

泛型编程

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

现在编写一个比较两个数大小的函数,事实上,我们要定义多个这样的函数才可以完成每一种内置类型的比较。

int max(const int& a, const int& b)
{
	return a > b ? a : b;
}

double max(const double& a, const double& b)
{
	return a > b ? a : b;
}

使用函数重载固然可以实现,但是也有不足:

  1. 函数重载只是类型不同,如果有新类型出现,就需要手动在定义一个函数
  2. 代码的可维护性比较低,如果需要在函数中添加一些额外的内容,需要在每个函数中进行修改

上面的代码中,两个函数只有参数不同,内容相同。

C++给出了解决办法—模板

函数模板

可以定义一个通用的函数模板,而不是为每一个类型都定义一个函数。一个函数模板就相当于一个蓝图,根据这个蓝图生成符合类型的函数。

模板的定义是以关键字 template开始,后跟一个模板参数列表,参数列表以逗号进行分割,用 <>这两个符号包起来

在模板定义中,模板参数列表不能为空

#include <iostream>

using std::cout;
using std::endl;

//int max(const int& a, const int& b)
//{
//	return a > b ? a : b;
//}
//
//double max(const double& a, const double& b)
//{
//	return a > b ? a : b;
//}

template <typename T>//or template <class T>,也可以用class,但是不能用struct
T max(const T& a, const T& b)
{
	return a > b ? a : b;
}

int main()
{
	int a = 1, b = 2;
	double a1 = 1.1, b1 = 2.2;
	cout << max(a, b) << endl;
	cout << max(a1, b1) << endl;

	return 0;
}

输出结果 2 2.2

函数模板的原理

在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用,比如double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码。

函数模板的实例化

编译器在调用函数模板的时候,会自动的根据实参类型来推断模板中的参数类型。

cout << max(1, 2) << endl;

比如这个,实参类型是int,编译器会推断出模板实参为int。

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

  1. 隐式实例化
template <typename T>
T Sub(const T& a, const T& b)
{
	return a - b;
}

int main()
{
	int a1 = 2, b1 = 1;
	double a2 = 2.2, b2 = 3.3;

	Sub(a1, b1);
	Sub(a2, b2);

	return 0;
}

在使用这个函数模板的时候,编译器会自动的推导出类型,这个过程叫做隐式实例化。

  1. 显示实例化
template <typename T>
T Sub(const T& a, const T& b)
{
	return a - b;
}

int main()
{
	int a1 = 2, b1 = 1;
	double a2 = 2.2, b2 = 3.3;

	Sub(a1, b2);
	return 0;
}

上面的代码中,模板参数列表只有一个T,在实例化的过程中,需要推导实参类型,通过a1会将T推导为int,而b2会将T推导为double类型,这个时候,参数列表中的T无法确定是int还是double。

此时就需要用户自己强转

Sub(a1, (int)b2);

或者使用显式实例化(在函数名后面的<>中指定模板参数的实际类型)

Sub<int>(a1, b2);

类模板

类模板的定义格式

template<class T1, class T2, ..., class Tn>
class 类模板名
{
// 类内成员定义
};
template <typename T>
class Stack
{
public:
	Stack(size_t capacity = 4)
		:_capacity(capacity)
		, _t(-1)
		,a(new T[capacity])
	{}

	~Stack();//当用模板的时候,在类中声明,在类外定义是如何写代码的?

	void Push(T x)
	{
		a[++t] = x;
	}

	void pop()
	{
		--t;
	}

private:
	T* a;
	int _capacity;
	int _t;
};

//类模板中函数放在类外进行定义时,需要加模板参数列表。
template <typename T>
Stack<T>::~Stack()
{
	if (a)
	{
		delete[] a;
	}
}

类模板的实例化

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

int main()
{
	Stack<int> stk;
	Stack<char> stk1;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值