初识模版--初阶

目录

1.泛型编程

2.函数模板

2.1概念

2.2函数模版使用格式

2.3函数模版原理

2.4函数模版的实例化

2.41概念

 2.42隐式实例化

2.43显示实例化

2.5模板参数的匹配原则

3.类模版

3.1类模版的使用格式

3.2类模版的实例化

结语:


1.泛型编程

概念:泛型编程是指编写与类型无关的通用代码,是代码复用的一种手段。而模板是泛型编程的基础,通过C++中的模版可以实现泛型编程。
那么泛型编程有什么好处呢?不妨以我们经常会使用到的交换两个变量的值的函数swap来说明:
void swap(char& p1, char& p2)
{
	int temp = p1;
	p1 = p2;
	p2 = temp;
}
void swap(int& p1, int& p2)
{
	int temp = p1;
	p1 = p2;
	p2 = temp;
}
void swap(double& p1, double& p2)
{
	int temp = p1;
	p1 = p2;
	p2 = temp;
}

看以上几个swap函数,对于不同类型变量的值交换,我们就需要写不同的swap函数实现这一功能,虽然对于函数重载来说,这并不是什么难事,但是,我们能明显发现使用重载函数应对这种情况是有很大缺点的:

1.使用重载的函数仅仅是类型不同,这就会导致代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数;2.此外,对于代码的可维护性也将降低,一个小的失误就可能导致所有的重载函数失效。

2.函数模板

2.1概念

函数模板代表了一个函数家族,函数模板与类型无关,在使用时函数的参数会被实例化成具体的类型,产生函数的特定类型版本以供用户使用。

2.2函数模版使用格式

函数模版的使用是由关键字template所定义的。其使用格式为:

  • template<typename/class 参数1, typename 参数2,......,typename 参数n>
  • 返回值类型 函数名(参数列表){}

其中,关键字typename和class都可使用用于定义模版参数,此外模版参数的个数也是可以按需添加的,各个参数之间使用逗号隔开,可以类比函数参数的使用。此外还需要注意的是,一个template所定义的模版只能用于一个函数,不能同时用于多个使用

使用模版函数的swap函数:

template<class T>
void sawp(T& p1, T& p2)
{
	int temp = p1;
	p1 = p2;
	p2 = temp;
}

2.3函数模版原理

关于函数模板的实现原理,我们可以将函数模版看做是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具,以其实模板就是将本来应该我们做的重复的事情交给了编译器。

在调用模版函数时,编译器会根据函数中传递实参的类型,推演生成带对应参数的函数。

2.4函数模版的实例化

2.41概念

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

 2.42隐式实例化

函数模版的隐式实例化是我们前面函数模版原理中所提到的用法,隐式实例化是通过函数模版调用时所传递的实参类型来进行实例化的,根据不同的实参类型,编译器会生成对应不同的函数版本。

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

以上面简单的函数模版为例,当我们传递的实参为int型时,编译器会生成一个参数为int型的函数供用户使用,同理,当传递实参为double型时,也会生成对应参数为double类型的函数。 

注意:隐式实例化时,传递实参不能够出现其传递类型的参数类型个数大于模版函数参数类型个数,这会导致参数无法匹配,使得隐式实例化失败。

 

2.43显示实例化

显示实例化是通过用户自己指定函数模版的参数类型来实现的,显示实例化的方法是:

在函数名后的<>中指定模板参数的实际类型

依然以模版函数Add为例: 

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

通过用户自己手动指定模版函数参数类型,即使出现实参中不匹配的情况,作为浮点型的3.14在作为实参传递给函数形参时,也会被视为是int型,强转成值为3的int型,最后得到函数返回值ret2 为5的结果。

 

2.5模板参数的匹配原则

1.一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数且不会发生了冲突。
int Add(int x, int y)
{
	return x + y;
}
template <class T>
T Add(T x, T y)
{
	return x + y;
}
int main()
{
	Add(1, 2);
	Add<int>(1, 2);//实例化函数模版参数类型为int
	return 0;
}

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

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

int main()
{
	//寻找到参数完全匹配的函数,使用存在的Add函数
	Add(1, 2);//有现成合适的使用现成的

	//1.未发现有合适匹配参数的Add函数
	//2.隐式实例化生成合适的Add函数,使用实例化生成的函数
	Add(1.12, 3.14);//没有现成的合适的,自己生成

	//1.未发现有合适匹配参数的Add函数
	//2.尝试隐式实例化生成更合适的函数,模版参数不匹配,生成失败
	//3.继续使用参数不太匹配的Add函数
	Add(1, 3.14);//没有现成合适的,但生成不了,将就使用不太合适的
	return 0;
}

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

前面我们也提到过,隐式实例化的过程不能出现实参与模版参数匹配错误的情况,则会导致实例化失败,而普通的函数,当实参与参数产生冲突时,会优先尝试隐式的类型转换,匹配参数,只有无法进行自动类型转换的情况,编译器才会报错。

3.类模版

类模版与函数模版在大致上是一致的,但也有些许差异,这里概念就不重复赘述了。

3.1类模版的使用格式

template<typename/class  参数1 , class 参数2, ..., class 参数n>
class类模板名
{
};

类模版使用的格式与函数模版没有太大区分,主要区分在于函数和类两者之间的声明定义。

template <class T>
class A
{
private:
	T _a;
public:
	A(T a)
		:_a(a)
	{}
};

需要注意的点:类中的函数如果是类中声明,类之外定义的情况

定义的函数需要指明类模版参数列表,所以还要多使用一个模版,使用类名<>的形式,在<>中写上模版参数。

template <class T>
class A
{
private:
	T _a;
public:
	A(T a);
};

template <class T>
A<T>::A(T a)//类中声明,类外定义
	:_a(a)
{}

3.2类模版的实例化

类的实例化与函数模版的实例化不同,类的实例化只能够显示实例化,这是因为类的实例化没有所谓实参的传递,编译器并不能通过实参去推演出模版参数的类型。

template <class T>
class A
{
private:
	T _a;
public:
	A(T a)
		:_a(a)
	{}
};
int main()
{
	A<int> a1(1);
	A<double> a2(3.14);
	return 0;
}

显示示例化后的两个类对象是两个不同类的对象,不属于同一个类。


结语

这节对于C++中模版的初阶知识介绍到这里就结束了,下期将继续带来C++相关的后续知识,感谢各位的观看,有错误或者需要改进的地方还请指出,如果有帮助的话,还请点个赞呀!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值