类模板详解
来源:互联网 发布:淘宝日系男装店铺推荐 编辑:程序博客网 时间:2024/04/27 01:21
模板类以下面的代码开头:
template <class Type>
可以使用关键字typename代替class:
template <typename Type> //newer choice
可以使用自己的通用类型名代替Type,其命名规则与其他标识符相同。
模板还可以包含多个类型参数
template<class T1,class T2>
通用类型标识符——例如这里的Type——称为类型参数(type parameter),这意味着它们类似于变量,但赋给它们的不能是数字,而只能是类型。
假设有类stack:
typedef unsigned long Item;
class stack
{private:
enum{MAX=10};
Item items[MAX];
...
public:
bool push(const Item & item);
...
};
建立模板类时,应将声明中标识Item的所有typedef替换为Type:
Item items[MAX];
bool push(const Item & item);
改为:
Type items[MAX];
bool push(const Type & item);
在模板类中使用模板成员函数,每个函数头都将以相同的模板声明打头:
template <class Type>
并使用通用类型名Type替换typedef标识符Item。另外,还需将限定符从stack::改为stack<Type>::。例如:
bool stack::push(const Item & item){...}
改为:
template <class Type>
bool stack<Type>::push(const Type & item){...}
在类外面,即指定返回类型或使用作用域解析符时,必须使用完整的stack<Type>
(如果在类声明中定义了方法即内联定义,则可以省略模板前缀和类限定符)
除非编译器实现了新的export关键字,否则将模板成员函数放置在一个独立的实现文件中将无法运行。因为模板不是函数,它们不能单独编译。模板必须与特定的模板实例化请求一起使用。为此,最简单的方法是将所有模板信息放在一个头文件中,并在要使用这些模板的文件中包含该头文件。
如果编译器实现了export关键字,则可以将模板方法定义放在一个独立的文件中,条件是每个模板声明都以export开始:
export template <class Type> // preface with export
class stack { ... };
然后按常规类的方式进行:
1.将模板类声明(包含关键字export)放在一个头文件中,并使用#include编译指令使程序能够使用这些声明。
2.将模板类的方法定义放在一个源代码文件中,在该文件中包含头文件,并使用工程文件(或其他等有效文件)使程序能够使用这些定义。
使用模板类
声明一个类型为模板类的对象,方法是使用所需的具体类型替换通用类型名:
stack<int> kernels; // create a stack of ints
stack<string> colonels; // create a stack of string objects
必须显示地提供所需的类型,这与常规的函数模板是不同的。可以将内置类型或类对象用作类模板的类型。但如果是指针,则需要对程序做重大修改,否则无法很好的工作。
数组模板范例和非类型参数
template <class T,int n>
class ArrayTP
{private: T ar[n];
... };
int指出n的类型为int,这种参数——指定特殊的类型而不是用作通用类型名,称为非类型(non-type)或表达式(expression)参数。
ArrayTP<double,12> eggweights;
将导致编译器定义名为ArrayTP<double,12>的类,并创建一个类型为ArrayTP<double,12>的eggweights对象。定义类时,编译器将使用double替换T,使用12替换n。
表达式参数可以是整形、枚举、引用或指针。模板代码不能修改参数的值,也不能使用参数的地址。所以,在ArrayTP模板中不能使用诸如n++和&n等表达式。在实例化模板时,用作表达式参数的值必须是常量表达式。
模板类可以用作基类,也可用作组件类,还可用作其他模板的类型参数。
例如,可以使用数组模板实现堆栈模板,也可以使用数组模板来构造数组——数组元素是基于堆栈模板的堆栈:
template<class T>
class Array { private: T entry; ... };
template<class Type>
class GrowArray: public Array<Type> {...}; //inheritance
template<class Tp>
class stack
{ Array<Tp> ar; //use an Array<> as a component
... };
...
Array < stack<int> > asi; // an array of stacks of int
(最后一条语句中,必须使用至少一个空白字符将两个>符号分开,以避免与>>操作符混淆)
递归使用模板
例如,对于前面的数组模板定义,可以这样使用:
ArrayTP< ArrayTP<int,5>,10> twodee;
这使得twodee是一个包含10个元素的数组,其中每个元素都是一个包含5个int元素的数组。与之等价的常规数组声明如下:
int twodee[10][5];
(在模板句法中,维的顺序与等价的二维数组相反)
默认类型模板参数
可以为类型参数提供默认值:
template<class T1,class T2 = int> class Topo {...};
如果省略T2的值,编译器将使用int:
Topo<double,double> m1; //T1 is double,T2 is double
Topo<double> m2; //T1 is double,T2 is int
(可以为类模板类型参数提供默认值,但不能为函数模板参数提供默认值。不过可以为非类型参数提供默认值,这对于类模板和函数模板都是适用的)
模板具体化
1.隐式实例化(implicit instantiation)
stack<int> stuff; // implicit instantiation
编译器在需要对象之前,不会生出类的隐式实例化:
stack<int> *pt; //a pointer,no object needed yet
pt = new stack<int>; //now an object is needed
第二条语句导致编译器生出类定义,并根据该定义创建一个对象。
2.显示实例化(explicit instantiation)
当使用关键字template并指出所需类型来声明类时,编译器将生成类声明的显示实例化。声明必须位于模板定义所在的名称空间中。例如:
template class stack<int>; //generate stack<int> class
将stack<int>声明为一个类。虽然没有创建或提及类对象,编译器也将生成类声明(包括方法定义)。和隐式实例化一样,也将根据通用模板来生成具体化。
3.显示具体化(explicit specialization)
template<> class classname<specialized-type-name> {...};
早期的格式为:
class classname<specialized-type-name> {...};
<specialized-type-name>改为特定的类型即可。
当具体化模板和通用模板都与实例化请求匹配时,编译器将使用具体化版本。
4.部分具体化(partial specialization)
// general temlpate
template <class T1,class T2> class Pair {...};
// specialization with T2 set to int
template <class T1> class pair<T1,int> {...};
关键字template后面的<>声明是没有被具体化的类型参数。因此,上诉第二个声明将T2具体化为int,但T1保存不变。如果指定所有的类型,则<>内将为空,这将导致显示具体化:
// specialization with T1 and T2 set to int
template <> class Pair<int,int> {...};
如果有多个模板可供选择,则编译器将使用具体化程度最高的模板:
Pair<double,double> p1; //use general Pair template
Pair<double,int> p2; //use Pair<T1,int> partial specialization
Pair<int,int> p3; //use Pair<int,int> explicit specialization
也可以通过为指针提供特殊版本来部分具体化现有的模板:
template<class T> //general version
class Feeb {...};
template<class T*> //pointer partial specialization
class Feeb {...}; //modified code
(如果提供的类型不是指针,则编译器将使用通用版本;如果提供的是指针,则编译器将使用指针具体化版本)
Feeb<char> fb1; //use general Feeb template,T is char
Feeb<char *> fb2; //use Feeb T* specialization,T is char
(如果没有进行部分具体化,则第二个声明将使用通用模板,将T转换为char *类型。如果进行了部分具体化,则第二个声明将使用具体化模板,将T转换为char。)
部分具体化特性使得能够设置各种限制:
//general template
template <class T1,class T2,class T3> class Trio{...};
//specialization with T3 set to T2
template <class T1,class T2> class Trio<T1,T2,T2> {...};
//specialization wih T3 and T2 set to T1*
template <class T1> class Trio<T1,T1*,T1*> {...};
根据上述声明,编译器将作出如下选择:
Trio<int,short,char *> t1; // use general template
Trio<int,short> t2; //use Trio<T1,T2,T2>
Trio<char,char *,char *> t3; //use Trio<T1,T1*,T1*>
成员模板
模板类将另一个模板类和模板函数作为其成员:
template <typename T>
class beta
{ private:
template <typename V> //nested template class member
class hold
{ ... };
hold<T> q; //template object
hold<int> n; //template object
public:
beta(T t, int i): q(t),n(i) {}
template<typename U>
U blab(U u,T t) { return (n.value() + q.value() ) * u / t; }
void show()const { q.show();n.show();}
};
如果编译器接受类外面的定义,则可如下编写代码:
template <typename T>
class beta
{private:
template <typename V> // declaration
class hold;
hold<T> q;
hold<int> n;
public:
beta(T t, int i): q(t),n(i) {}
template<typename U> // declaration
U blab(U u,T t);
void show()const { q.show();n.show();}
};
//member definition
template <typename T>
template <typename V>
class beta<T>::hold
{ ... };
//member definition
template <typename T>
template <typename V>
U beta<T>::blab(U u,T t)
{
return (n.value() + q.value() ) * u / t;
}
template <typename T>
template <typename V>
而不是:
template <typename T,typename V>
定义还必须指出hold和blab是beta<T>类的成员,这是通过使用作用域解析操作符来完成的。
将模板用作参数
模板还可以包含本身就是模板的参数:
template <template <typename T> class Thing>
class Crab
{private:
Thing<int> s1;
Thing<double> s2;
... };
上述中模板参数是template <typename T> class Thing,其中template <typename T> class 是类型,Thing是参数。假设有下面的声明:
Crab<king> legs;
为使上述声明被接受,模板参数king必须是一个模板类,其声明与模板参数Thing的声明匹配:
template <typename T>
class king { ... };
Crab的声明声明了两个对象:
Thing<int> s1;
Thing<double> s2;
前面的legs声明将用king<int>替换Thing<int>,用king<double>替换Thing<double>。
简而言之,模板参数Thing将被替换为声明Crab对象时被用作模板参数的模板类型。
混合使用模板参数和常规参数,如:
template <template <typename T> class Thing,typename U,typenameV>
class Crab
{ private:
Thing<U> s1;
Thing<V> s2;
... };
成员s1和s2可以存储的数据类型为通用类型,而不是硬编码指定的类型。
- 类模板详解
- C++类模板详解
- C++类模板详解
- C++类模板详解
- C++类模板详解
- 类模板详解
- C++类模板详解
- C++类模板详解
- 类模板详解
- C++类模板详解
- c++类模板详解
- C++类模板详解
- C++类模板详解
- C++类模板详解
- C++类模板详解
- C++类模板详解!
- C++模板类详解
- 实例详解C++类模板
- ASP读取ini文件的实现方法
- Java JNI深度分析与实践
- 如何查看SQL Server2000的版本号
- windows的程序,想让它随系统启动运行
- 阶段性突破-2009年写的第七篇日记
- 类模板详解
- SQL查询语句精华大全
- Ravens rise to 8th most valuable NFL franchise
- ASP读取ACCESS数据库随机记录的方法
- Section 2.2 Runaround Numbers
- 详解javascript类继承机制的原理
- 用API OleLoadPicture通过IStream来加载JPG、GIF格式的图片
- Studying note of GCC-3.4.6 source (92)
- 竞争优势-2009年写的第八篇日记