C++类模板

类模板

//模板不是函数,不能单独编译,所以要将模板信息放在同一个头文件中
template<class T>
class Stack
{
private:
    enum{MAX=10};
    T items[MAX];
    int top;
public:
    Stack();
    bool isempty();
    Stack(const Stack& st);
    Stack& operator=(const Stack& st);
};
template<class T>
Stack<T>::Stack(){top=0;}

template<class T>
bool Stack<T>::isempty(){return top==0;}

template<class T>
Stack<T>::Stack(const Stack& st){} //参数中也可以使用Stack<T>

template<class T>
Stack<T>& Stack<T>::operator=(const Stack<T> & st){} //参数中也可以使用Stack
//原型中返回类型是引用,但是在定义是需要定义为Stack<T>
//原型是缩写形式,只能应用在类中
//即指定返回类型或者使用作用域解析符时,必须使用Stack<T>

使用模板类

Stack<int> a;
Stack<string> b;
//类模板必须显式提供所需的类型

非类型参数

template<class T,int n> //n为非类型参数或者表达式参数
class ArrayTp
{
private:
	T ar[n]; //未使用new,使用的是栈
public:
	ArrayTp() {};
	explicit ArrayTp(const T& v);
	T& operator[](int i);
};

template<class T, int n>
ArrayTp<T, n>::ArrayTp(const T& V)
{
	for (int i = 0; i < n; i++)
	{
		ar[i] = n;
	}
}

template<class T, int n>
T& ArrayTp<T, n>::operator[](int i)
{
	return ar[i];
}
  1. 非类型参数可以是整形、枚举、引用或者指针。

template<class T,double n>是不合法的,可以是template<class T,double *n>

  1. 模板代码不能修改非类型参数的值,也不能使用参数的地址。所以不能使用n++或者&n之类的,
  2. 实例化模板时,非类型参数的值必须是常量表达式
  3. 每种数组大小都会生成自己的模板
//这会生成两个独立的类声明
ArrayTp<double,10> v1;
ArrayTp<double,20> v2;

使用非类型参数的优缺点:

优点:可以直接使用数组声明即内存栈来维护数组,执行速度偏快

缺点:构造函数法(不使用非类型参数)更通用,因为数组大小是存储在定义中,而不是硬编码,这样可以将不同尺寸的数组进行相互赋值,且也可以动态改变数组的大小

模板多功能性

可以将常规类的技术用于模板类,如模板类可作为基类、作为类型参数等。

template<class T>
class Array
{
private:
    T entry;
};

template<class T>
class GrowArray::public Array<Type>
{};

template<class T>
class Stack
{
    Array<T> ar;
};

Array<Stack<int>> a;//c++11之前,最后两个符号必须隔开
  1. 递归使用模板

ArrayTp< ArrayTp<int,5>,10> twoTpint twoTp[10][5]

  1. 使用多个类型参数
template<class T1,class T2>
class Pair
{
private:
	T1 a;
	T2 b;
	...
};
  1. 默认类型模板参数
template<class T1,class T2=int>
class Topp{};

注意:可以为类模板类型参数提供默认值,不能为函数模板参数提供默认值。但是函数模板和类模板都可以为非类型参数提供默认值

模板的具体化

  1. 隐式实例化
//编译器在需要对象之前不会生成类的隐式实例化
ArrayTp<double,20> * pt;//声明一个pointer,目前还不需要对象
pt = newArray<double ,20>;//隐式实例化
  1. 显示实例化
//使用template来声明显示实例化
template class ArrayTp<string,100>;//将ArrayTp<string,100>生命为一个类
//该声明必须位于模板定义所在的名称空间中
  1. 显示具体化
template<> class ArrayTp<cosnt char*>{...};

ArrayTp<> a;//使用泛型模板
ArrayTp<const char*> b;//使用具体化模板
  1. 部分具体化
template<class T1> class Pair<T1,int>{};
//template后面的<>中是未被具体化的类型参数 如果该<>中为空,则是3.显示具体化

//如果有多个模板可供选择,编译器使用具体化程度最高的模板
Pair<double,double> p1;//使用Pair<T1,T2>
Pair<double,int> p2;//使用Pair<T1,int>
Pair<int,int> p3;//使用Pair<T1,int>

为指针提供特殊版本来实现部分具体化

template<class T>
class Feeb{...}

template<class T*>
class Feeb{...}

Feeb<char> fb1;//使用通用模板 T为char
Feeb<char *> fb2;//使用T*,T为char

部门具体化可实现各种限制

//通用模板
template<class T1,class T2,class T3> class Trio{};
//T2与T3相同
template<class T1,class T2> class Trio<T1,T2,T2>{};
//T2、T3是T1的指针类型
template<class T1> class Trio<T1,T1*,T1*>{};

Trio<int,short,char*> t1;//通用模板
Trio<int,short> t2;//Trio<T1,T2,T2>
Trio<char,char*,char*> t3;//Trio<T1>

成员模板

template<class T>
class A
{
private:
	template<class V>
	class B{}; //嵌入类
	B<T> q;
public:
	A(T t, int i) {};
	template<class U>
	U fun(U u, T t) {return u;}
};

A<double> a(3.5, 3); 
a.fun(10, 2.3);//自动推断,U为int
//有些编译器是不能在模板中声明B类和fun方法,在外面定义,而有些可以,如果可以需要这样定义:
template<class T>
class A
{
private:
	template<class V>
	class B;
	B<T> q;
public:
	A(T t, int i) {};
	template<class U>
	U fun(U u, T t);
};

template<class T>
	template<class V>
	class A<T>::B {};

template<class T>
	template<class U>
	U A<T>::fun(U u, T t) { return u; }

//由于T、V、U是嵌套关系,所以需要这样定义:
template<class T>
	template<class V>
//而不能这样:
template<class T,class V>  
//另外还需要使用作用域限定符来指出B与fun是A的成员

模板作为参数

template <template <typename T> class T2>

模板参数是template class T2,其中template class是类型,T2是参数。

#定义方式有点类似于非类型参数   template<class T,int n>

//可以混合使用模板参数和常规参数
template<template <typename T> class T2,typename U,typename V>
class Carb{};
posted @ 2022-06-16 09:20  步、步、为营  阅读(31)  评论(0编辑  收藏  举报