c++模板
如何编写一个通用的加法函数呢?
通过宏实现
1 没有参数检测,导致安全性不高
2 不能像函数进行调试,编译期间代码替换,当代码很长时会增加代码量
3 宏只能处理整数或枚举类型的数据,其他外置类型的数据处理不了
#include<iostream>
using namespace std;
#define ADD(x,y) ((x) + (y))
int main()
{
cout<<ADD(1,3)<<endl;
cout<<ADD(1.3,2.5)<<endl;
cout<<ADD('A','G')<<endl;
system("pause");
return 0;
}
函数重载实现加法函数
int Add(const int &_iLeft, const int &_iRight)
{
return (_iLeft + _iRight);
}
float Add(const float &_fLeft, const float &_fRight)
{
return (_fLeft + _fRight);
}
int main()
{
cout << Add(1, 3) << endl;
cout << Add(1.3f, 2.5f) << endl;
system("pause");
return 0;
}
这种方法的缺点
1 只要有新类型出现,就要实现相应函数
2 除类型外,所有函数的函数体相同,代码复用率不高
3 如果只是返回值类型不同,函数重载不能解决问题
4 一个方法有问题,所有的方法都有问题,不易维护
通过多态实现
使用公共基类,将需要用到的虚函数代码放在公共的基础类里面,通过基类的对象指针进行调用,派生类可重写也可不重写。
class B
{
public:
virtual int add(int _x, int _y)
{
return (_x + _y);
}
virtual float add(float _x, float _y)
{
return (_x + _y);
}
};
class INT_ADD :public B
{};
class FLOAT_ADD :public B
{};
int main()
{
B *b;
INT_ADD i;
FLOAT_ADD f;
b = &i;
cout << b->add(1, 3) << endl;
b = &f;
cout << b->add(1.8f, 3.6f) << endl;
system("pause");
return 0;
}
借助基类虚函数编写缺点
1 写各种类型加法函数,只要有新类型旧需要重写对应函数
2 对于以后实现的许多派生类都必须调用各自特定的基类虚函数,代码维护困难
模板解决
模板分类: 函数模板 / 类模板 分别允许用户构造函数模板 、类模板
使用函数模板实现加法函数
template<typename T>
T ADD(T x, T y)
{
return (x + y);
}
int main()
{
cout << ADD(1, 3) << endl;
cout << ADD(1.3, 2.5) << endl;
system("pause");
return 0;
}
优点:利用模板可用显著减少冗余信息,能大幅节约程序代码,进一步提高面向对象程序的可重用性和可维护性
函数模板
实际是建立了一个通用函数,函数返回类型和形参类型不具体指定,
用一个虚拟的类型代表,这个通用函数成为函数模板。
一般格式 template <typename 类型参数>
返回类型 函数名 模板形参表
函数模板的使用
函数模板的实例化:
当我们调用函数模板时,编译器用函数实参来为我们推断模板实参,此模板实参为我们实例化一个特定的函数。
实例化:显示实例化, 隐士实例化
template <typename T>
T ADD(T x,T y)
{
return (x+y);
}
int main()
{
cout<<ADD(1,3)<<endl;//隐式实例化
cout<<ADD<float>(1.3f,2.5f)<<endl;//显式实例化
system("pause");
return 0;
}
注意:自定义类型不能直接调函数模板进行实例化,除非自己实现重载
注意:模板被编译了两次
1 实例化之前,检查模板代码本身,查看是否出现语法错误
2 实例化期间,检查模板代码,查看是否所有调用都有效
模板函数的使用规则
模板函数可定义为内联函数
template <typename T>
inline T ADD(T x,T y)
{
return (x+y);
}
inline关键字必须放在模板形参表之后,返回值之前,不能放在template之前
在template语句与函数模板定义语句之间不允许插入别的语句
函数模板不足:不能自动进行类型转换,需要生成代码,所以编译速度慢
模板参数
1 实参推演 :从函数实参确定模板形参类型和值
2类型形参转换
1. const转换
接收const引用或const指针的函数可用分别用非const对象的引用和指针调用
template <typename T>
T ADD(const T& x,const T& y)
{
return (x+y);
}
2. 数组或函数到指针的转换
数组实参将当作指向其第一个元素,函数实参当作指向函数类型的指针
template <typename T>
T ADD(const T* x,const T* y)
{
return (*x + *y);
}
int main()
{
int a[5] = {0};
int b[5] = {5,4,3,2,1};
cout<<"a + b = "<<ADD(a,b)<<endl;
system("pause");
return 0;
}
模板参数的分类
函数模板有两种类型参数:模板参数和调用参数
模板形参:类型形参和非类型形参
函数模板的重载
1 同一般函数一样,函数模板也可用重载
template<typename T>
T Max(const T& left, const T& right)
{
return left>right? left:right;
}
template<typename T>
T Max(const T& a, const T& b, const T& c)
{
return Max(Max(a, b), c);
};
int main()
{
cout<<Max(1,2)<<endl;
cout<<Max(1.2,3.4,5.6)<<endl;
system("pause");
return 0;
}
运行结果 //2
//5.6
注意:函数的所有重载版本的声明都应该位于该函数被调用位置之前
函数模板的特化(具体化)
1、有时候不是所有可能被实例化的模板都是合适的,某些情况下,通用模板定义对于某个类型可能是完全错误的,就需要对模板特例化(具体化)
template<typename T>
int Compare(T s1, T s2)
{
if (s1 > s2)
{
return 1;
}
else if (s1 < s2)
{
return -1;
}
else
return 0;
}
template<>//函数模板的特化
int Compare<const char*>(const char* s1, const char*s2)
{
return strcmp(s1,s2);
}
int main()
{
const char* str1 = "abcd";
const char* str2 = "ghf";
cout << Compare(str1, str2) << endl;
system("pause");
return 0;
}
函数模板特化形式:
template<>
返回值 函数名 (参数列表)
{
//函数体
}
注意:特化的声明必须与特定的模板相匹配
给函数模板传参时一定要与特化参数列表中的参数格式一模一样,否则有可能即使写了特化,运行过程中也不会调用。
类模板
类模板 :以关键字temeplate开头,后接模板形参表
类模板格式: template
typedef int DataType;
//typedef char DataType;
class SeqList
{
private :
DataType* _data ;
int _size ;
int _capacity ;
};
【模板类实现顺序表】
template<typename T>
class Seqlist
{
public:
Seqlist();
~Seqlist();
private:
int _size;
int _capacity;
T* _data;
};
template <typename T>
Seqlist<T>::Seqlist()
:_size(0)
, _capacity(10)
, _data(new T[_capacity])
{}
template<typename T>
Seqlist<T>::~Seqlist()
{
delete[]_data;
}
void test1()
{
Seqlist<int> sl1;
Seqlist<double>sl2;
}
模板类的实例化
SeqList<int > sl1;
SeqList<double > sl2;
当定义上述两种类型的顺序表,编译器会使用int,double分别代替模板形参,重新编写Seqlist类,最后创建Seqlist和Seqlist的类