1).模板类的继承,继承还是和正常类一样的
例如:/*0.基本概念:泛型编程范式GP:模板也叫参数类型多态化。 在编译时期确定,相比面向对象的虚函数多态,能够有更高的效率。 泛型编程是从一个抽象层面描述一种类型的算法,不管容器类型是什么,是一种不同于OOP的角度来抽象具体算法。 C++0X目前对GP的支持的趋势来看,确实如此,auto/varadic templates这些特性的加入象征着C++ GP的形式正越来越转 向一种更纯粹的泛性语法描述。 GP的一个主要的弱点就是它不是二进制可复用的,源码基本是公开的,因为编译时候才决定具体类型,决定了类型后就不能更改了不像OOP一样拥有关闭开放的原则。 */ #ifndef FUNCTIONTEMPPLATE_H #define FUNCTIONTEMPPLATE_H // 1.1 模板的定义和实现,template和typename声明后,函数内直接使用;函数不支持template参数列表是空的,类才支持 #include "FunctionTempplate.cpp" template<typename T1,typename T2> void SwapTwoType(T1 &t1, T2 &t2) { T1 temp = t1; t1 = (T1)t2; t2 = (T2)temp; } // 1.2. 非泛型类型参数,直接使用就可以了,可以传入该类型的常量或者变量 // 关于形参:如果是类型形参,我们就知道该形参表示未知类型,如果是非类型形参,我们就知道它是一个未知值。 template<typename T> bool TestOver(T t1, int t2) { return int(t1) > t2; } // 2.泛型类型定义后,函数内部直接使用即可,类型变量符合作用域规则,包括不可重定义或覆盖,可见作用域 template<typename T> void SwapOneType(T &t1, T &t2) { T temp = t1; t1 = t2; t2 = temp; } // 函数不支持template是空的,类才支持,也不支持SwapImp<double &t1, int &t2>这样的写法 //template<> //void SwapImp<double &t1, int &t2> //{ // double temp = t1; // t1 = t2; // t2 = (int)temp; //}; // 3.函数模板声明和实现分离原因和做法 // 将Template函数的声明和实现分离,声明在.h中,实现在.cpp中,其中.h需要包含.cpp // 需要分离的原因有: //真正原因:1.简化条理化代码,实现声明和实现分离,高内聚低耦合,对外提供简单的接口,对内高内聚 //几乎不会出现,STL都没有分离:2.将模板类的声明与实现都放在.h中(在多个cpp中使用不同模板参数时可能会引起重复定义的编译错误) template<typename T> void SwapOneTypeImp(T &t1, T &t2); #endif
template<typename T> void SwapOneTypeImp(T &t1, T &t2) { T temp = t1; t1 = t2; t2 = temp; }
#ifndef CLASSTEMPLATE_H #define CLASSTEMPLATE_H // 1.1.声明类模板,例如: // template<class 形参名,class 形参名,…> class 类名 template<typename T> class CMathOperation { // 2.1使用类模板的类型声明,包括数据成员,和函数参数、函数返回值都可以直接使用泛型类型 // 类型变量符合作用域规则,包括不可重定义或覆盖,可见作用域 public: T Add(T &t1, T &t2) { return t1 + t2; } T Multi(T &t1, T &t2); T Min(T &t1, T &t2); // 1.2.非泛型类型参数,直接使用就可以了,可以传入该类型的常量或者变量 // 关于形参:如果是类型形参,我们就知道该形参表示未知类型,如果是非类型形参,我们就知道它是一个未知值。 bool bOverMax(T &t1, int maxNum); private: T a; T b; }; // 2.2 在类声明外定义类函数(在CPP中也是可以的,但是包含情况就会发生变化),格式如下: // template<模板形参列表> 函数返回类型 类名<模板形参名>::函数名(参数列表){函数体} template<typename T> T CMathOperation<T>::Multi(T &t1, T &t2) { return t1*t2; } // 3.这种模板分离的方式缺点:如果这个模板类的cpp有非模板的定义,能够有效实例化,那么会导致重复包含定义而出错 // 所以有些模板的实现和分离放到了.tcc格式或者.inl格式的文件中(这些格式的文件都是来自于txt文本的不能从cpp直接修改得到), // 在.h中直接包含进去,代码中就可以直接包含ClassTemplate.h了,避免包含.cpp奇怪的行为;这种方式编辑时候需要修改下后缀。 #include "ClassTemplate.tcc" // 1.3 针对类模板声明附加特性: // 模板不能是空模板参数类型,但是声明了正常的声明了一个模板类后,如果要对这个模板类进行更加具体的限定那么可以 // 重新定义这个模板的细节,这个时候模板参数类型可以为空,但是这个时候重定义的名称一定要相同,使用的模板参数也是要定义了的。 // 例如template<> class BinaryNumericTraits1会报错,template<> class BinaryNumericTraits<T1, double>也会报错,正确定义如下: template< typename T1, typename T2 > class BinaryNumericTraits { public: typedef T1 OpResult; }; template<> class BinaryNumericTraits<int, double> { public: typedef double OpResult; }; template<> class BinaryNumericTraits<double, int> { public: typedef double OpResult; }; #endif
// 类模板可以直接在.h中声明,在文本文件中定义 template<typename T> T CMathOperation<T>::Min(T &t1, T &t2) { return t1 > t2 ? t2 : t1; } template<typename T> bool CMathOperation<T>::bOverMax( T &t1, int maxNum ) { if( t1 > maxNum ) { return true; } return false; } // 如果这个模板类的cpp有非模板的定义,能够有效实例化,那么会导致重复包含定义而出错 // 所以有些模板的实现和分离放到了.tcc格式或者.inl格式的文件中,在.h中直接包含进去 //int g_TestValue = 100; //bool IsNegtiveValue() //{ // if( g_TestValue < 0 ) // { // return true; // } // return false; //}
#include "FunctionTempplate.h" // 3.关于模板声明和实现分离后的调用原理 // 类的声明必须在实现的前面,所以使用的时候包含cpp就可以把.h和.cpp中定义的内容一并包含了,其实是放在同一个文件中一个意思 // 原因是:C++标准明确表示,当一个模板不被用到的时侯它就不该被实例化出来, // cpp中没有将模板类实例化,所以实际上.cpp编译出来的t.obj文件中关于模板实例类型的一行二进制代码, // 于是连接器就傻眼了,只好给出一个连接错误。 // 所以这里将.cpp包含进来,其实还是保持模板的完整抽象,main中可以对完整抽象的模板实例化。 // 这种模板分离的方式缺点:如果这个模板类的cpp有非模板的定义,能够有效实例化,那么会导致重复包含定义而出错 // 所以有些模板的实现和分离放到了.tcc格式或者.inl格式的文件中(这些格式的文件都是来自于txt文本的不能从cpp直接修改得到), // 在.h中直接包含进去,代码中就可以直接包含ClassTemplate.h了,避免包含.cpp奇怪的行为。 #include "ClassTemplate.h" #include <iostream> using namespace std; int n1,n2; double dValue1, dValue2; void ResetTestData() { n1 = 10; n2 = 100; dValue1 = 1.5f; dValue2 = 16.5f; } void DisplayTestData(int nCount) { cout<<"---------------Result of count: "<<nCount<<"-------------"<<endl; cout<<"n1= "<<n1<<",n2= "<<n2<<",dValue1= "<<dValue1<<",dValue2= "<<dValue2<<endl; } extern void TestSwapFunc(); void main() { // 一、函数模板使用 cout<<"-------------函数模板使用------------"<<endl; TestSwapFunc(); ResetTestData(); // 4.1 函数模板的使用,实参推演和类型匹配 // 实参推演是:模板函数不支持类型显式实例化声明的,直接用实参变量实例化调用就好。 // 类型匹配是:一种类型的只能是一种类型,不能两种类型传入一种类型的模板函数中,是类型引用别名的不能用实例值传入。 //SwapOneType(<int>(n1), <int>(n2));语法错误 : 缺少“)”(在“<”的前面),Swap(T1 &,T2 &)”: 应输入 2 个参数,却提供了 0 个 //SwapTwoType(<int>(n1), <int>(n2)); 语法错误 : 缺少“)”(在“<”的前面),Swap(T1 &,T2 &)”: 应输入 2 个参数,却提供了 0 个 //SwapOneType(2, 3);不能将参数 1 从“int”转换为“int &” //SwapTwoType(2, 3);不能将参数 1 从“int”转换为“int &” SwapTwoType(n1, n2); // OK DisplayTestData(1); //SwapTwoType(2, 3); // 错误,SwapTwoType”: 不能将参数 1 从“int”转换为“int &” ResetTestData(); SwapTwoType(dValue1, dValue2); // OK,直接实例化 DisplayTestData(2); ResetTestData(); SwapTwoType(n1, dValue1);// 也OK,直接实例调用因为是支持两个类型的,只是dValue1的1.5f转换为n1时候被截取了整型变为了1 DisplayTestData(3); ResetTestData(); //SwapOneType(n1, dValue1);// 编译错误,因为只有一个类型,void SwapOneType(T &,T &)”: 模板 参数“T”不明确 SwapOneType(dValue1, dValue2); // OK,同样的一个类型实例化 DisplayTestData(4); ResetTestData(); SwapOneTypeImp(dValue1, dValue2); DisplayTestData(5); // 4.2 非泛型类型参数,直接使用就可以了,可以传入该类型的常量或者变量 int nCurNum = 10; int nMaxValue = 100; bool bTestOver = TestOver(nCurNum, nMaxValue); // 二、类模板使用 // 4.1 模板类的使用,类对象显式类型声明不支持实参推演,类的成员函数要求实参推演和类型匹配 cout<<"-------------类模板使用------------"<<endl; CMathOperation<int> oprObj; // OK,必须要显式类型指定,不能用函数模板的实参推演 int a = 10; int b = 15; int nAddResult = oprObj.Add(a,b); // OK,类成员函数参数不能使用显式类型指定了 int nMultiResult = oprObj.Multi(a,b);// OK,类外部定义也是可以的 //int nAddResult2 = add.Add(<int>(a),<int>(b));// NO 显式类型指定是错误的 //float fTestValue = 15.0f; //int nAddResult = add.Add(a, fTestValue);// NO 和函数模板一样也是要求类型匹配的 /*float fValue1 = 1.5f; float fValue2 = 3.5f; float fRes3 = oprObj.Add(fValue1, fValue2);*/ // NO 对象是int类型的,因为类型匹配,故其它类型的参数传入报错 int minNum = oprObj.Min(a,b); // 4.2 非泛型类型形参,只能是简单类型 int nValue = 10; bool bOver = oprObj.bOverMax(nValue, 100); // OK int nMaxValue1 = 100; bool bOver1 = oprObj.bOverMax(nValue, nMaxValue1); // 也是OK的,只要类型匹配就可以了 // 非泛型类型参数,直接使用就可以了,可以传入该类型的常量或者变量 const int nMaxValue2 = 100; bool bOver2 = oprObj.bOverMax(nValue, nMaxValue2); // 也是OK的,只要类型匹配就可以了 while(1); }
//本文件主要针对于不同的模板传入不同的参数会导致编译重定义问题 // 几乎不会出现,STL都没有分离: // 将模板类的声明与实现都放在.h中(在多个cpp中使用不同模板参数时可能会引起重复定义的编译错误) #include "FunctionTempplate.h" // 将模板cpp多次包含是不会参数多重包含的,因为编译时候这个模板的cpp并不能有效的实例化。 //#include "ClassTemplate.tcc" void TestSwapFunc() { float fTestValue1 = 1.0f; float fTestValue2 = 1000.0f; double dValue3 = 10.001f; SwapOneType(fTestValue1, fTestValue2); SwapTwoType(fTestValue1, dValue3); }