程序员成长之旅——泛型模板

程序员成长之旅——泛型模板


大家都知道C++是C的超集,具有面向对象编程的能力。然而许多程序员可能还不知道,C++不仅是一个面向对象程序语言,它还适用于泛型编程。今天我们通过一些问题来认识一下泛型编程。

泛型编程

1.举例说明什么是泛型编程
泛型编程指编写完全一般化并可以重复使用的算法,其效率与针对某特定数据类型而设计的算法相同。所谓泛型,是指具有在多种数据类型上皆可操作的含意,在C++中实际上就是使用模板实现。
举一个简单的例子,比如我们要比较两个数的大小,这两个数的类型可能是int、double、float。一般编程是我们可能用函数重载来实现。

int max(int a, int b)
{
	return a > b ? a : b;
}
double max(double a, double b)
{
	return a > b ? a : b;
}
float max(float a, float b)
{
	return a > b ? a : b;
}

用函数模板的话

template<class T>//class也可以用typename替换
T max(T a, T b)
{
	return a > b ? a : b;
};

在这里的class不代表对象的类,而是类型。
从而可以得到以下结论
1.重载的函数仅仅只是类型不同,代码的复用率较低,只要有新类型出现时,就需要增加对应的函数。
2.代码的维护性比较低,一个出错可能所有的重载均出错。
3.泛型编程可以极大的增加代码的重用性。
2.函数模板与类模板分别是什么
函数模板是一种抽象的函数定义,它代表一类同构函数。类模板是一种更高层次的抽象的类定义。
函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化必须由程序员在程序中显示的指定。
3.使用模板有什么缺点?如何避免
模板是节省时间和避免代码重复的很好的办法。我们可以只输入一个类模板,就能让编译器实例化所需要的很多个特定类及函数。类模板的成员函数只有被使用的时候才会被实例化,所以只有在每一个函数都在实际中被使用时,我们才会得到这些函数。
但是有时我们一不小心使用模板就会导致代码膨胀。看下面的例子

template<class T,int num>
class A
{
public:
	void work()
	{
		cout << "work()" << endl;
		cout << num << endl;
	}
};
int main()
{
	A<int, 1>v1;
	A<int, 2>v2;
	A<int, 3>v3;
	v1.work();
	v2.work();
	v3.work();
	return 0;
}

类模板A取得一个类型参数T,并且它还有一个类型为int的参数、一个非类型参数。且是合法的。
从上面可以看出来虽然他们做了同样的事情,但是他们却有着不同的二进制代码。这就是所说的由于模板导致的代码膨胀。从而会影响程序的运行效率。
如何避免呢?
有一个原则,就是把C++模板中与参数无关的代码分离出来。也就是让与参数无关的代码只有一份复制,上面的代码可修改为

template<class T>
class Base
{
public:
	void work(int num)
	{
		cout << "work()" << endl;
		cout << num << endl;
	}
};
template<class T,int num>
class Derived : public Base<T>
{
public:
	void work()
	{
		Base<T>::work(num);
	}
}
int main()
{
	A<int, 1>v1;
	A<int, 2>v2;
	A<int, 3>v3;
	v1.work();
	v2.work();
	v3.work();
	return 0;
}

从而我们得出结论:
模板的缺点:不当地使用模板会导致代码膨胀,即二进制代码臃肿而松散,会严重影响程序的运行效率。
解决方法:把C++模板中与参数无关的代码分离出来。
4.类模板的实例化
模板类的实例化个数是由类型参数的种类决定的。
5.解释什么是模板的特化
模板的特化分为两类:函数模板的特化和类模板的特化。
(1)函数模板的特化:当函数模板需要对某些类型进行特别处理时,称为函数模板的特化。例如:

template<class T>
bool IsEqual(T t1,T t2)
{
	return t1 == t2;
}

int main()
{
	char str1[] = "Hello";
	char str2[] = "Hello";
	cout << IsEqual(1,1) << endl;
	cout << IsEqual(str1,str2) << endl;//输出0
	return 0;
}

上面输出为0,是因为这个函数模板只是简单比较了传入参数的值,而传入的是指针,指针不相等,因此打印的是0.显然,这与我们的初衷不符。因此,这个函数模板需要对char*类型进行特别处理,即特化:

template<>
bool IsEqual(char* t1,char* t2)
{
	return strcmp(t1,t2) == 0;
}

这样的话,当IsEqual函数的参数类型为char*时,就会调用IsEqual特化的版本,而不会再由函数模板实例化。
(2)类模板的特化:与函数模板类似,当类模板内需要对某些类型进行特别处理时,使用类模板的特化。例如:

template<class T>
class compare
{
public:
	bool IsEqual(T t1,T t2)
	{
		return t1 == t2;
	}
};

int main()
{
	char str1[] = "Hello";
	char str2[] = "Hello";
	compare<int> c1;
	compare<char *> c2;
	cout << c1.IsEqual(1, 1) << endl;//比较两个int类型的参数
	cout << c2.IsEqual(str1,str2) << endl;//比较两个char*类型的参数
	return 0;
}

这里和上面的问题几乎一样。因此,需要类模板的特化:

template<>
class compare<char *> //特化(char*)
{
public:
	bool IsEqual(char* t1,char* t2)
	{
		return strcmp(t1,t2) == 0;
	}
};

6.部分模板特例化和全部模板特例化有什么区别
模板有两种特例化:部分模板特例化和全部模板特例化。
全部模板特例化就是模板中的模板参数全部被指定为确定的类型。

template<class A,class B,class C>
class X{};    //(a)

template<>
class X<int, float, string>{}; //(b)

若编译器遇到X<int,float,string>的模板实例化请求,则使用特例化的版本(b)。其它都是(a)。
部分模板特例化就是模板中的模板参数类型没有被全部确定,需要编译器在编译时进行确定。它通常有两种情况:
(1)对部分模板参数进行特例化

template<class B, class C>
class X<int,B,C>{}; // (c)

当编译器实例化时遇到第一个模板实参是int的时候,就用特例化版本。
(2)使用具有某一特征的类型,对模板参数进行特例化:

template<class T>
class Y{}; //(d)

template<class T>
class Y<T*> {}; //(e)

当编译器遇到Y<int*>时,使用(e),Y<float*>也是(e)。而其他则用普通版本。
它们还可以混合使用,比如:

template<class A,class B>
class Z{};

template<typename A>
class Z<A&,char>{};

7.模板的分离编译
什么是分离编译
一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。
模板的声明定义一般都放在同一个文件.hpp中,这是防止了链接错误。
8.类型萃取
类型萃取使用模板技术来萃取类型(包含自定义类型和内置类型)的某些特性,用以判断该类型是否含有某些特性,从而在泛型算法中来对该类型进行特殊的处理用来提高效率或者其他。

类型萃取依靠的就是 模板的特化
参考网址
9.模板的优缺点
优点
(1)模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生的。
(2)增加了代码的灵活性
缺点
(1)模板会导致代码的膨胀问题,也会导致编译的时间变长
(2)出现模板编译错误时,错误信息非常凌乱,不易定位错误

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

从零出发——

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值