c++ 模板

模板

1. 泛型编程

如何实现一个通用的交换函数呢?
(使用函数重载可以实现,但是代码重复率比较低,只要有新类型出现时,就需要用户自己增加对应的函数,并且代码可维护性较差,一个出错可能导致所有的重载均出错。)

c++中,存在一个模具,通过给这个模具填充不同的材料(类型),来获得不同材料的铸件(即生成具体类型的代码)。

模板是泛型编程的基础。

模板分为:
1.函数模板
2.类模板


2.函数模板

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

语法格式:
template<typename T1 , typename T2,…,typename Tn>
返回值类型 函数名(参数列表){}
(typename 换成class 也可以,但是不能换成struct)

template <class T>
void swap(T& a, T& b)
{
	T tmp = a;
	a = b;
	b = tmp;
}
//模板实现交换函数

原理:
函数模板只是一个蓝图,它本身并不是函数,是编译器用来产生特定具体函数的模具。


int main()
{
	int a1 = 10;
	int a2 = 20;
	double d1 = 10.00;
	double d2 = 20.00;

	Swap(a1, a2);//编译器在编译时推演为int类型
	Swap(d1, d2);//编译器在编译时会推演为double类型

	return 0;
}
  • 函数模板的实例化

实例化分为:1.隐式实例化 2.显式实例化

对于1. 编译器根据实参推演模板参数的实际类型。

int main()
{
	int a1 = 10;
	int a2 = 20;
	double d1 = 10.00;
	double d2 = 20.00;

	Swap(a1, a2);//编译器在编译时推演为int类型
	Swap(d1, d2);//编译器在编译时会推演为double类型

	return 0;
}

需要注意的是:

template <class T>
T Add(T x, T y)
{
	return x + y;
}

int main()
{
	int a1 = 10;
	double d1 = 20.00;
	Add(a1, d1);

	return 0;
}

这段代码无法通过编译 因为在编译期间,当编译器实例化时,将a1推演为int ,d1推演为double,但模板参数列表只有一个T!编译器无法确定此处到底将T确定为int 还是double。
此时有两种处理方式:

  1. 强制类型转换
int main()
{
	int a1 = 10;
	double d1 = 20.00;
	Add(a1, (int)d1);

	return 0;
}
  1. 使用显式实例化

对于 显式实例化:
函数名后的<>中指定模板参数的实际类型

int main()
{
	int a1 = 10;
	double d1 = 20.00;
	Add<int>(a1, d1);

	return 0;
}
  • 模板参数的匹配原则
    1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
int Add(int a, int b)
{
	return a + b;
}//专门处理 int 加法的函数

template<class T>
T Add(T left, T right)
{
	return left + right; //通用的add函数
}


int main()
{
	Add(1, 2);  //与非模板函数匹配,编译器不需要特化 
	Add<int>(1, 2); // 调用编译器特化的Add版本

	return 0;
}
  • 2.对于非模板函数和同名函数模板,如果其他条件相同,在调用时会优先调用非模板函数,而不会从该模板产生出一个实例。 如果模板可以产生一个具有更好匹配的函数,那么将选择该模板。
int Add(int a, int b)
{
	return a + b;
}//专门处理 int 加法的函数

template<class T, class Y>
T Add(T left, Y right)
{
	return left + right; //通用的add函数
}


int main()
{
	Add(1, 2);  //与非模板函数匹配,编译器不需要特化 
	Add<int>(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的ADD函数

	return 0;
}

3.类模板

语法:
template<class T1, class T2,…,class Tn>
class 类名
{ //类内部
}

template<class T >
class Vector
{
public:
	Vector(int capacity = 4)
		:_a(new T[capacity])
		,_size(0)
		,_capacity(capacity)
	{}

private:
	T* _a;
	int _size;
	int _capacity;
};

类模板函数放在类外面定义时,需要加模板参数列表。

template<class T >
class Vector
{
public:
	Vector(int capacity = 4)
		:_a(new T[capacity])
		,_size(0)
		,_capacity(capacity)
	{}
	~Vector();
	void PushBack(T x) {};


private:
	T* _a;
	int _size;
	int _capacity;
};


template<class T>
Vector<T>::~Vector()
{
	if (_a)
	{
		delete[] _a;
		_size = _capacity = 0;
	}
}

template<class T>
void Vector<T>::PushBack(T x)
{
	

}

  • 类模板实例化
int main()
{
	Vector<int> i1;
	Vector<double> d2;

	return 0;
}

4.模板分离编译:



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

}// template.h


template<class T>
T Add(const T& left, const T& right);
// test.cpp

int main()
{
	Add(1, 2);

	return 0;
}

该编译无法通过!

在test.cpp中,编译器没有看到对Add模板函数的实例化,因此不会生成具体的加法函数。 在test.obj中调用 Add(1,2)才会 在链接时寻找其地址,但是这哥函数没有实例化生成的具体代码,因此链接时报错。

解决方案:
1. 将声明和定义放到一个文件 “xxx.hpp" 或 “xxx.h” 中。
2. 模板定义的位置显式实例化(不实用,不推荐)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值