8.7.1 定义类模板(1)
我们将选择一个简单的示例来说明如何定义和使用类模板,并且不过多考虑因误用而可能出现的错误,那样将使问题复杂化。假设要定义几个可以存储大量数据样本的类,每个类都应当提供一个求出所存储样本中最大值的Max()函数。该函数类似于在第6章讨论函数模板时介绍的Max()函数。可以定义一个类模板,用来生成可以存储任何类型样本的CSamples类。
- template <class T>
- class CSamples
- {
- public:
- // Constructor definition to accept an array of samples
- CSamples(const T values[], int count)
- {
- m_Free = count < maxSamples Count : maxSamples; // Don't exceed the array
- for(int i = 0; i < m_Free; i++)
- m_Values[i] = values[i]; // Store count number of samples
- }
- // Constructor to accept a single sample
- CSamples(const T& value)
- {
- m_Values[0] = value; // Store the sample
- m_Free = 1; // Next is free
- }
- // Default constructor
- CSamples() : { m_Free = 0 } // Nothing stored, so first is free
- // Function to add a sample
- bool Add(const T& value)
- {
- bool OK = m_Free < 100; // Indicates there is a free place
- if(OK)
- m_Values[m_Free++] = value; // OK true, so store the value
- return OK;
- }
- // Function to obtain maximum sample
- T Max() const
- {
- // Set first sample or 0 as maximum
- T theMax = m_Values[0];
- for(int i = 1; i < m_Free; i++) // Check all the samples
- if(m_Values[i] > theMax)
- theMax = m_Values[i]; // Store any larger sample
- return theMax;
- }
- private:
- static const size_t maxSamples = 100; // Maximum number of sample
- T m_Values[maxSamples]; // Array to store samples
- int m_Free; // Index of free location in m_Values
- };
为了指出正在定义的是模板而非简单的类,在关键字class和类名CSamples之前,插入关键字template和尖括号内的类型形参T。该语法实质上与第6章定义函数模板的语法相同。形参T是类型变量,它将在声明类对象时由具体类型代替。类定义中出现形参T的任何位置,都将由对象声明中指定的类型代替,这将创建一个对应于指定类型的类定义。可以指定任何类型(基本数据类型或类类型),但指定的类型必须在类模板的上下文中有意义。任何用来根据模板实例化某个类的类类型,都必须已经定义过模板的成员函数处理本类对象时要使用的所有运算符。例如,如果类没有实现operator>(),则不能使用上面的CSamples类模板。一般来说,如果需要的话,则可以在类模板中指定多个形参,稍后再来讨论这种可能性。
回到本示例上来,存储样本的数组的类型指定为T。因此,该数组将成为声明CSamples对象时为T指定的那种类型的数组。可以看出,不仅在Add()和Max()函数中,而且还在类的两个构造函数中也使用了类型T。当使用该模板实例化类对象时,同样替换掉构造函数中出现的这些T。
构造函数支持创建空对象、只有一个样本的对象以及用样本数组进行初始化的对象。Add()函数允许一次一个地在对象中添加样本。也可以重载这个函数,以允许添加样本数组。在Add()函数中和在接受样本数组的构造函数中,类模板提供了基本的措施来防止超过m_Values数组的最大容量。
如前所述,理论上可以创建可处理任何数据类型的CSamples类的对象:int类型、double类型、CBox类型或任何已经定义过的类类型。在实践中,这种可能性并不意味着必定能够编译所创建的对象,且像我们预期的那样工作。实际情况完全取决于模板定义所做的事情,通常一个模板仅适用于特定的类型范围。例如,Max()函数隐含地认为>运算符可以用于被处理的任何类型。如果实际情况不是这样,则不能编译程序。无疑,通常定义的模板只是为了处理某些类型而非此外的其他类型,但无法限制应用到模板上的类型。
模板成员函数
我们或许希望将类模板成员函数的定义放在模板定义的外部。实现该功能的语句不是特别明显,因此我们来看一下应该如何做。以常规方式将函数声明放在类模板定义的内部。例如:
- template<class T>
- class CSamples
- {
- // Rest of the template definition...
- T Max() const; // Function to obtain maximum sample
- // Rest of the template definition...
- }
此处的代码将Max()函数声明为类模板的成员,但没有定义该函数。现在,需要为这个成员函数的定义创建单独的函数模板,创建时必须使用模板类的名称加上尖括号内的形参,以标识函数模板所属的类模板:
- template<class T>
- T CSamples<T> ::Max() const
- {
- // Set first sample as maximum
- T theMax = m_Values[0];
- for(int i = 1; i < m_Free; i++) // Check all the samples
- if(m_Values[i] > theMax)
- theMax = m_Values[i]; // Store any larger sample
- return theMax;
- }