8.5.2 根据类模板创建对象
当我们使用函数模板定义的函数时,编译器能够根据使用的实参类型生成函数。函数模板的类型形参是通过使用特定的函数隐式确定的。类模板有些不同。为了以类模板为基础创建对象,我们必须在声明中指定类名后面的类型形参。
例如,为了声明一个CSamples<>对象来处理double类型的样本,需要将声明写成下面这样:
- CSamples<double> myData(10.0);
该语句定义了一个CSamples<double>类型的对象,它可以存储double类型的样本。该对象是用值为10.0的一个样本创建的。
试一试:类模板
我们可以根据CSamples<>模板,创建一个存储CBox对象的对象。这是没有问题的,因为CBox类实现了重载大于运算符的operator>()函数。利用下面代码中给出的main()函数,我们可以练习类模板的用法:
- // Ex8_07.cpp
- // Using a class template
- #include <iostream>
- using std::cout;
- using std::endl;
-
- // Put the CBox class definition from Ex8_06.cpp here...
-
- // CSamples class template definition
- template <class T> class CSamples
- {
- public:
- // Constructors
- CSamples(const T values[], int count);
- CSamples(const T& value);
- CSamples(){ m_Free = 0; }
-
- bool Add(const T& value); // Insert a value
- T Max() const; // Calculate maximum
-
- private:
- T m_Values[100]; // Array to store samples
- int m_Free; // Index of free location in m_Values
- };
-
- // Constructor template definition to accept an array of samples
- template<class T> CSamples<T>::CSamples(const T values[], int count)
- {
- m_Free = count < 100 count:100; // 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
- template<class T> CSamples<T>::CSamples(const T& value)
- {
- m_Values[0] = value; // Store the sample
- m_Free = 1; // Next is free
- }
-
- // Function to add a sample
- template<class T> bool CSamples<T>::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
- template<class T> T CSamples<T>::Max() const
- {
- T theMax = m_Free m_Values[0] : 0;
// Set first sample or 0 as maximum - 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;
- }
-
- int main()
- {
- CBox boxes[] = { // Create an array of boxes
- CBox(8.0, 5.0, 2.0), // Initialize the boxes...
- CBox(5.0, 4.0, 6.0),
- CBox(4.0, 3.0, 3.0)
- };
-
- // Create the CSamples object to hold CBox objects
- CSamples<CBox> myBoxes(boxes, sizeof boxes / sizeof CBox);
-
- CBox maxBox = myBoxes.Max(); // Get the biggest box
- cout << endl // and output its volume
- << "The biggest box has a volume of "
- << maxBox.Volume()
- << endl;
- return 0;
- }
我们应该用本章前面Ex8_06.cpp文件中CBox类的定义代替该程序开头相应的那行注释。不必担心支持CBox对象与double类型数值进行比较的operator>()函数,因为本示例不需要该函数。除默认构造函数以外,该模板的所有成员函数都是通过单独的函数模板定义的,本例只是为了给出一个说明类模板用法的完整示例。
在main()函数中,我们创建了一个包含三个CBox对象的数组,然后使用该数组初始化一个可以存储CBox对象的CSamples对象。CSamples对象的声明基本上与普通类对象的声明相同,但在模板类名称后面增加了以尖括号包围的类型形参。
该程序产生下面的输出:
- The biggest box has a volume of 120
注意,当我们创建类模板的实例时,不能理解成用于创建函数成员的那些函数模板的实例也被创建。编译器只创建程序中实际调用的那些成员函数的模板实例。事实上,我们的函数模板甚至可以包含编码错误,而且只要不调用该模板生成的成员函数,编译器就不会报错。我们可以利用该示例证实这一点。试着给Add()成员的模板引入几处错误。该程序仍然能够编译和运行,因为它没有调用Add()函数。
我们可以尝试修改上面的示例,看看用不同类型的模板实例化类时,可能会发生什么事情。
注意:
如果给类的构造函数添加一些输出语句,其结果会让我们对此感到惊讶。CBox的构造函数被调用了103次!看一看在main()函数中究竟发生了什么。我们首先创建了一个包含三个CBox对象的数组,因此发生了三次调用。然后创建了一个容纳这些对象的CSamples对象,但CSamples对象包含的数组有100个CBox类型的变量,因此需要再调用默认构造函数100次,每个数组元素需要一次。当然,maxBox对象将由编译器提供的默认复制构造函数创建。