福州市建设管理处网站,网站设计平台及开发工具,快速创建一个网站,记事本网页制作教程在平时的工作和学习过程中#xff0c;经常会用到泛型#xff0c;这里对泛型和模板进行一下梳理#xff0c;以便理解和使用。
模板关键字 template。为什么要使用模板?
假如设计一个两个参数的函数,用来求两个对象的乘积,在实践中我们可能需要定义n多个函数
int multipli…在平时的工作和学习过程中经常会用到泛型这里对泛型和模板进行一下梳理以便理解和使用。
模板关键字 template。为什么要使用模板?
假如设计一个两个参数的函数,用来求两个对象的乘积,在实践中我们可能需要定义n多个函数
int multiplication(int a,int b){return ab;}
char multiplication(char a,char b){return ab;}
float multiplication(float a,float b){return ab;}
...这些函数几乎相同,唯一的区别就是形参类型不同,在这种情况下,不必定义多个函数,只需要在模板中定义一次即可。 在调用函数时系统会根据实参的类型来取代模板中的虚拟类型从而实现不同的函数功能。
模板与泛型
模板是泛型编程的基础泛型编程是指独立与任何类型的代码编程。 模板是一种对类型进行参数化的工具有两种形式函数模板和类模板。
函数模板
函数模板 针对仅参数类型不同的函数 定义格式
templatetypename Type
Type funName(Type val)
{//Code
}模板形参表使用typename或者class定义都可以没有任何区别为了区分类的定义一般使用typename. //对于任意类型的两个对象相加的函数模板
templatetypename T
T multiplication(T a,T b)
{return a * b;
}函数模板调用 对于函数模板有两种调用方式 显示类型调用 需要在函数调用处的函数名后面加上类型参数 multiplication(2,4); 自动类型推导 根据参数的类型进行推导但是两个参数的类型必须一致否则会报错。 multiplication(‘a’,‘c’); 那么需要传两个不一样的参数要怎么做呢写两个模板类型即可:
template typename T,typename U
auto multiplication(T a,U b)
{return a * b;
}函数模板和普通函数
函数模板不提供隐式类型转换必须是严格匹配。 普通函数提供隐式类型转换。 templatetypename T
void multiplication(T a,T b)
{cout模板函数 a * b endl;
}
show(A,65); //“void showSum(T,T)”: 未能从“char”为“T”推导 模板 参数
showint(A,65); //显示指定模板类型后‘A’可以转换到int
函数模板和普通函数构成重载时调用规则:
templatetypename T
void multiplication(T a,T b)
{cout模板函数(2)a*bendl;
}
templatetypename T
void sum(T a,T b,T c)
{cout模板函数(3)a*b*cendl;
}
void sum(int a,int c)
{cout普通函数a*cendl;
}
void Test()
{multiplication(1,2); //当函数模板和普通函数参数都符合时优先选择普通函数multiplication(1,2); //若显示使用模板函数则使用类型列表multiplication(3.0,4.2); //如果函数模板产生更好的匹配则使用函数模板multiplication(5.0,6.5,8.2); //只有一个合适multiplication(a,12); //调用普通函数可以隐式类型转换}类模板
类模板与函数模板的定义和使用类似。 有时有两个或多个类其功能是相同的仅仅是数据类型不同如下面语句声明了一个类 //模板的参数类型定义写在类的定义之前在类里的任意位置都可以使用
templatetypename T
class Factory
{
public:Factory(T val) :_value(val){}void Factory(){cout _value endl;}
private:T _value;
};类模板用于实现类所需数据的类型参数化 类模板在表示如数组、表、图等数据结构显得特别重要这些数据结构的表示和算法不受所包含的元素类型的影响。
单个类模板语法
定义一个类模板非常简单重要的是如何去用类模板定义对象
如果没有指定模板的参数列表编译器是会报错的
Factory d(20); //error C155: “Factory”: 使用 类 模板 需要 模板 参数列表指定参数列表只需要在类名的后面加上类型即可
Factorystring d(aaa);
Factorystring d1(string(bbb));实际上,类模板的使用就是将类模板实例化成一个具体的类。 类模板不代表一个具体的、实际的类而代表一类类。
只有那些被调用的成员函数才会产生这些函数的实例化代码。对于类模板成员函数只有在被使用的时候才会被实例化。显然这样可以节省空间和时间
如果类模板中含有静态成员那么用来实例化的每种类型都会实例化这些静态成员。
来个示例
#includeiostream
#includestring
using namespace std;templatetypename T
class Object
{
public:Object(int size) :_capacity(size),_size(0),_base(nullptr){if (_capacity 0)_capacity 1;_base new T[_capacity]{T()};}T operator[](int index){if (index 0 || index _capacity){//return T(); //不能返回临时对象的引用对于int() 是一个0throw std::out_of_range(Object 越界); //抛异常是最合适的}return _base[index];}
private:T* _base;int _size;int _capacity;
};
int main()
{Objectint arr(10);for (size_t i 0; i 10; i){cout arr[i] ;}cout endl;Objectstring arr1(10);arr1[0] string(cdef);for (size_t i 0; i 10; i){cout arr1[i] ;}return 0;
}继承中的类模板——类模板派生普通类
子类从模板类继承的时候,需要让编译器知道父类的数据类型具体是什么(数据类型的本质:如何分配内存空间)
templatetypename T
class Factory
{
public:Factory(T val) :_value(val){}void factory(){cout _value endl;}
protected:T _value;
};
class A:public Factoryint //指定具体类型
{
public:using Factoryint::Factory;void show(){cout A _value endl;}
};继承中的类模板——类模板派生模板
templatetypename T
class Factory
{
public:Factory(T val) :_value(val){}void factory(){cout _value endl;}
protected:T _value;
};
templatetypename U
class A:public FactoryU
{
public:using FactoryU::Factory;void show(){cout A _value endl;}
};代码看起来没有问题但是在子类中使用父类的成员会提示找不到标识符
void show()
{cout A _value endl; //error C2065: “_value”: 未声明的标识符
}解决办法 1通过this指针访问this-_value 2通过父类访问 Factory U ::_value
模板特化
提到特化这个概念就想到泛化的概念。模板函数的T参数只能传入类类型的参数特化函数的参数只能传入对应的参数类型。
函数模板特化
假设有一个比较两个对象的模板函数
templatetypename T
int compare(T a, T b)
{cout T endl;return a b ? 0 : (a b ? 1 : -1);
}对于支持operator 和 operator操作的类型 包括基本的intfloatdouble等类型是完全没有问题的但是它不能用来比较字符串(char*)因为这个函数比较的是串指针而不是字符串本身。
cout compare(A, a) endl; //类型是const char*比较的是地址,需要做特化版本才能比较特化版本:
template //必须写不然就是重载函数而不是函数模板特化版本了
inline int compare(const char* str1, const char* str2)
{cout const char * endl;return strcmp(str1, str2);
}或者//test.h
template
int compare(const char* str1, const char* str2);
//test.cpp
template
int compare(const char* str1, const char* str2)
{cout 特化 const char * endl;return strcmp(str1, str2);
}
这样就能正确比较字符串了。
类模板特化
全特化所有类型模板参数都用具体类型代表特化版本模板参数列表为空 template
templatetypename T,typename U
struct Test
{void show(){cout非特化版本endl;}
};
//全特化版本
template
struct Testint,int
{void show(){coutint,int特化版本endl;}
};
//特化版本可以有任意多个
template
struct Testdouble,string
{void show(){coutdouble,string特化版本endl;}
};
//测试
int main()
{Testint,int t;t.show(); //int,int特化版本Testdouble, string t1; t1.show(); //double,string特化版本Testchar, char t2;t2.show(); //非特化版本return 0;
}局部特化(偏特化)指定一部分模板参数用具体类型代替
从模板参数数量上
//从模板参数数量上
templatetypename T,typename U
struct Test
{void show(){cout非特化版本endl;}
};
//局部特化
templatetypename U
struct Testdouble,U
{void show(){cout非特化版本endl;}
};
//测试
int main()
{Testchar,string tt;tt.show(); //局部特化版本return 0;
}
从模板参数范围上(int - int)
//从模板参数范围上
templatetypename T
struct Test
{void show(){cout非特化版本endl;}
};
//const T
templatetypename T
struct TestT
{void show(){coutT特化版本endl;}
};
//T*
templatetypename T
struct TestT*
{void show(){coutT*特化版本endl;}
};
//测试
int main()
{Testint test;test.show(); //非特化版本Testint* test1;test1.show(); //T*特化版本Testint test2;test2.show(); //T特化版本return 0;
}
总结
泛型编程和面向对象编程都依赖与某种形式的多态。面向对象编程的多态性在运行时应用于存在继承关系的类。在泛型编程中编写的代码可以用作多种类型的对象。面向对象编程所依赖的多态性称为运行时多态性而泛型编程所依赖的多态性称为编译时多态性或参数式多态性。 通过上面这些介绍相信会对模板与泛型理解的更深入。