珠海公司网站设计,wordpress 3.8.1,柳州建站公司,谷歌广告代理公司C中关于模板泛型编程问题#xff1a; 问题引入#xff1a;何编写一个通用加法函数#xff1f; #xff08;1#xff09;使用函数重载#xff0c;针对每个所需相同行为的不同类型重新实现它
int Add(const int _iLeft, const int_iRight)
{
return (_iL…C中关于模板泛型编程问题 问题引入何编写一个通用加法函数 1使用函数重载针对每个所需相同行为的不同类型重新实现它
int Add(const int _iLeft, const int_iRight)
{
return (_iLeft _iRight);
}
float Add(const float _fLeft, constfloat _fRight)
{
return (_fLeft _fRight);
}
【缺点】
1、只要有新类型出现就要重新添加对应函数。
2、除类型外所有函数的函数体都相同代码的复用率不高
3、如果函数只是返回值类型不同函数重载不能解决
4、一个方法有问题所有的方法都有问题不好维护。 2使用公共基类将通用的代码放在公共的基础类里面 【缺点】
1、借助公共基类来编写通用代码将失去类型检查的优点
2、对于以后实现的许多类都必须继承自某个特定的基类代码维护更加困难。 3使用特殊的预处理程序
#define ADD(a, b) ((a) (b))
【缺点】
不是函数不进行参数类型检测安全性不高 综上所述的问题我们需要引入泛型编程即为需要的函数或者类编写一个模板在实用的时候实例化即可。那么什么是泛型编程什么是模板 一、 泛型编程
泛型编程编写与类型无关的逻辑代码是代码复用的一种手段。模板是泛型编程的基础。
二、 函数模板
函数模板代表了一个函数家族该函数与类型无关在使用时被参数化根据实参类型产生函数的特定类型版本。
模板函数定义的格式templatetypename T1, teypename T2, ……….typename Tn
函数返回值 函数名参数列表
{ . . . . . .
} Eg: templatetypename T
T Add( T left, T right )
{ return leftright;
} template和typename 为关键字T为模板形参的名字可随意命名。 typename是用来定义模板参数关键字也可以使用class。建议尽量使typename。 实例化模板是一个蓝图它本身不是类或者函数编译器用模板产生指定的类或者函数的特定类型版本产生模板特定类型的过程称为函数模板实例化 注模板被编译了两次
① 实例化之前检查模板代码本身查看是否出现语法错误如遗漏分号
②在实例化期间检查模板代码查看是否所有的调用都有效如实例化类型不支持某些函数调用 实参推演
从函数实参确定模板形参类型和值的过程称为模板实参推断多个类型形参的实参必须完全匹配
类型形参转换
一般不会转换实参以匹配已有的实例化相反会产生新的实例。
编译器只会执行两种转换
1、const转换接收const引用或者const指针的函数可以分别用非const对象的引用或者指针来调用 2、数组或函数到指针的转换如果模板形参不是引用类型则对数组或函数类型的实参应用常规指针转换。数组实参将当做指向其第一个元素的指针函数实参当做指向函数类型的指针。 Eg:
templatetypename T
void FunTest1(const T* t)
{ coutFunTest1();*tendl;
}
templatetypename T
void FunTest2(const T t)
{ coutFunTest2();tendl;
}
templatetypename T
void FunTest3(T t1, T t2)
{ coutFunTest3()endl;
} int Add(int a, int b)
{ return ab;
}
int main()
{ int A 10; int* pA A; FunTest1(pA); int b 20; int pB b; FunTest2(pB); int array1[10]; int array2[20]; FunTest3(array1, array2); FunTest3(Add,Add); system(pause); return 0;
} 模板参数函数模板有两种类型参数模板参数和调用参数。 1 模板形参
a、模板形参名字只能在模板形参之后到模板声明或定义的末尾之间使用遵循名字屏蔽规则。 b、模板形参的名字在同一模板形参列表中只能使用一次。
Templatetypename T, typenameT
Void FunTest(T t1, T t2)
{}; c、 所有模板形参前面必须加上class或者typename关键字修饰并且两个关键字可以混用。
d、在函数模板的内部不能指定缺省的模板实参。
Templatetypename T
T Add(T int , T) //编译出错。
{};
非模板类型参数 非模板类型形参是模板内部定义的常量在需要常量表达式的时候可以使用非模板类型参数。 注
1、模板形参表使用括起来。
2、和函数参数表一样跟多个参数时必须用逗号隔开类型可以相同也可以不相同。
templatetypenameT, typename V ……. 3、模板形参表不能为空模板特化的时候模板参数为空。 4、模板形参可以是类型形参也可以是非类型新参类型形参跟在class和typename后 5、模板类型形参可作为类型说明符用在模板中的任何地方与内置类型或自定义类型使用方法完全相同可用于指定函数形参类型、返回值、局部变量和强制类型转换。 6、模板形参表中class和typename具有相同的含义可以互换使用typename更加直观。但关键字typename是作为C标准加入到C中的旧的编译器可能不支持。 模板函数重载 注
1、函数的所有重载版本的声明都应该位于该函数被调用位置之前
2、一个非模板函数可以和一个同名的函数模板同时存在而且该函数模板还可以被实例化为这个非模板函数。
3、对于非模板函数和同名函数模板如果其他条件都相同在调动时会优先调动非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数那么将选择模板。
4、显式指定一个空的模板实参列表该语法告诉编译器只有模板才能来匹配这个调用而且所有的模板参数都应该根据实参演绎出来。
5、模板函数不允许自动类型转换但普通函数可以进行自动类型转换。 新的问题引入
string s1 addfhgj; string s2 addfghjkl;
Max(s1,s2); //未能从“const std::string”为“const std::move_iterator_RanIt ”推导 模板 参数。
故模板有一些特殊的情况不能处理就需要引入模板的特化什么是模板的特化 模板函数特化 有时候并不总是能够写出对所有可能被实例化的类型都最合适的模板在某些情况下通用模板定义对于某个类型可能是完全错误的或者不能编译或者做一些错误的事情。 Eg:比较两个字符串的大小 调试之后发现比较的是字符串地址的大小而不是字符串的大小。因此需要对模板函数进行特化以处理特殊的情况。这就需要对模板类的特殊情况进行处理-------模板特化 注意
在模板特化版本的调用中实参类型必须与特化版本函数的形参类型完全匹配如果不匹配编译器将为实参模板定义中实例化一个实例。
特化不能出现在模板实例的调用之后应该在头文件中包含模板特化的声明然后使用该特化版本的每个源文件包含该头文件。 三、 模板类
1、 模板类的定义格式 templateclass 形参名1, class 形参名2, ...class 形参名n
class 类名
{ ... }; // 使用模板方式实现动态顺序表
templatetypename 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 Test()
{ SeqListints1; SeqListdoubles2; SeqListchars3;
} 2、 模板类的实例化
只要有一种不同的类型编译器就会实例化出一个对应的类。
SeqListint sl1;
SeqListdouble sl2;
当定义上述两种类型的顺序表时编译器会使用int和double分别代替模板形参重新编写SeqList类最后创建名为SeqListint和SeqListdouble的类。 1模板参数实现容器适配器。
#include List.h
//底层使用List 容器 template class T, class container listT //模板参数
class Queue
{
public: void push(const T x) { return _con.PushBack(x); } void pop() { return _con.PopFront(); } const T GetHead() { return _con.Front(); } const T GetTail() { return _con.Back(); } bool IsEmpty() { return _con.Empty(); }
private: container _con;
};
void Test2()
{ Queueint q1;//使用缺省的模板参数构造对象 Queueint, Listint q2;//使用模板参数构造对象
}
2模板的模板参数实现容器适配器。 template class T, templateclass class container List //使用模板的模板参数
class Queue
{
public: void push(const T x) { return _con.PushBack(x); } void pop() { return _con.Pop(); } const T GetHead() { return _con.Front(); } const T GetTail() { return _con.Back(); } bool IsEmpty() { return _con.Empty(); }
private: containerT _con;
};
void Test1()
{
Queueintq1;//使用缺省的模板类的模板参数构造对象 Queueint,List q2;//使用模板的模板参数构造不同类型对象
} 3非类型的类模板参数
template typename T, size_t MAX_SIZE 10//带缺省模板参数
//templatetypename T, double MAX_SIZE 10.0 // “double”: 非类型模板参数“MAX_SIZE”的类型非法 class Array
{
public : Array();
private : T _array [MAX_SIZE];
int _size ;
};
template typename T, size_t MAX_SIZE
Array T,MAX_SIZE::Array() : _size(0) {} void Test()
{ Arrayint a1; Arrayint , 20 a2;
}
4类模板的特化:
//顺序表类的部分实现
template typename T
class SeqList
{
public : SeqList(); ~ SeqList();
private : int _size ; int _capacity ; T* _data ;
};
templatetypename T
SeqList T:: SeqList() : _size(0) , _capacity(10) , _data(new T[ _capacity]) { coutSeqListT:: SeqList() endl; }
templatetypename T
SeqList T::~ SeqList()
{ delete[] _data ;
} //全特化
template
class SeqList int
{
public : SeqList(int capacity); ~ SeqList();
private : int _size ; int _capacity ; int* _data ;
};
// 特化后定义成员函数、成员函数不再需要模板形参列表
SeqList int:: SeqList(int capacity) : _size(0) , _capacity(capacity ) , _data(new int[ _capacity]) { coutSeqListint endl; }
SeqList int::~ SeqList()
{ delete[] _data ;
}
void test1 ()
{ SeqListdouble sl2; SeqListint sl1(2);
} //偏特化部分特化 template typename T1, typename T2
class Data
{
public : Data();
};
template typename T1, typename T2
DataT1 , T2::Data()
{ coutDataT1,T2endl;
}
// 局部特化第二个参数为某个具体的类型如int 、 double 等
template typename T1
class Data T1, int
{
public : Data(); };
template typename T1
DataT1 , int::Data()
{
coutDataT1,intendl;
} 下面的例子可以看出偏特化并不仅仅是指特化部分参数而是针对模板参数更进一步的条件限制所设计出来的一个特化版本。 // 局部特化两个参数为指针类型
template typename T1, typename T2
class Data T1*, T2*
{
public : Data(); };
template typename T1, typename T2
DataT1 *, T2*:: Data()
{ coutDataT1*,T2*endl;
}
// 局部特化两个参数为引用
template typename T1, typename T2
class Data T1, T2
{
public : Data(const T1 d1, const T2 d2); };
template typename T1, typename T2
DataT1 , T2:: Data(const T1 d1, const T2 d2) { coutDataT1,T2endl; }
void Test()
{ Datadouble , int d1; Dataint , double d2; Dataint *, int* d3; Dataint, int d4(1, 2);
}