作者:jicanmeng
时间:2017年06月05日
和函数模板一样,我们先看一个程序,共三个文件:IntArray.h,DoubleArray.h和class_template_before.cpp:
//IntArray.h #ifndef INTARRAY_H #define INTARRAY_H #include <assert.h> // for assert() class IntArray { private: int m_length; int *m_data; public: IntArray() { m_length = 0; m_data = nullptr; } IntArray(int length) { assert(length > 0); m_data = new int[length]; m_length = length; } ~IntArray() { delete[] m_data; } void Erase() { delete[] m_data; // We need to make sure we set m_data to 0 here, otherwise it will // be left pointing at deallocated memory! m_data = nullptr; m_length = 0; } int& operator[](int index) { assert(index >= 0 && index < m_length); return m_data[index]; } int getLength() { return m_length; } }; #endif
DoubleArray.h文件内容如下:
//DoubleArray.h #ifndef DOUBLEARRAY_H #define DOUBLEARRAY_H #include <assert.h> // for assert() class DoubleArray { private: int m_length; double *m_data; public: DoubleArray() { m_length = 0; m_data = nullptr; } DoubleArray(int length) { assert(length > 0); m_data = new double[length]; m_length = length; } ~DoubleArray() { delete[] m_data; } void Erase() { delete[] m_data; // We need to make sure we set m_data to 0 here, otherwise it will // be left pointing at deallocated memory! m_data = nullptr; m_length = 0; } double& operator[](int index) { assert(index >= 0 && index < m_length); return m_data[index]; } int getLength() { return m_length; } }; #endif
class_template_before.cpp文件内容如下:
//class_template_before.cpp #include <iostream> #include "IntArray.h" #include "DoubleArray.h" int main() { IntArray intArray(12); DoubleArray doubleArray(12); for (int count = 0; count < intArray.getLength(); ++count) { intArray[count] = count; doubleArray[count] = count + 0.5; } for (int count = intArray.getLength() - 1; count >= 0; --count) { std::cout << intArray[count] << "\t" << doubleArray[count] << '\n'; } return 0; }
以下是运行结果:
[jicanmeng@andy tmp]$ ./a.out
11 11.5
10 10.5
9 9.5
8 8.5
7 7.5
6 6.5
5 5.5
4 4.5
3 3.5
2 2.5
1 1.5
0 0.5
[jicanmeng@andy tmp]$
可以看出,上面代码和使用函数模板之前的代码一样,都存在代码冗余的问题。为了解决这个问题,c++引入了类模板。如下面代码所示:
Array.h文件内容如下:
//Array.h #ifndef ARRAY_H #define ARRAY_H #include <assert.h> // for assert() template <class T> // This is a template class, the user will provide the data type for T class Array { private: int m_length; T *m_data; public: Array() { m_length = 0; m_data = nullptr; } Array(int length) { assert(length > 0); m_data = new T[length]; m_length = length; } ~Array() { delete[] m_data; } void Erase() { delete[] m_data; // We need to make sure we set m_data to 0 here, otherwise it will // be left pointing at deallocated memory! m_data = nullptr; m_length = 0; } T& operator[](int index) { assert(index >= 0 && index < m_length); return m_data[index]; } // The length of the array is always an integer // It does not depend on the data type of the array int getLength(); }; // member functions defined outside the class need their own template declaration template <typename T> int Array<T>::getLength() { return m_length; } // note class name is Array<T>, not Array #endif
class_template_after.cpp文件内容如下:
//class_template_after.cpp #include <iostream> #include "Array.h" int main() { Array<int> intArray(12); Array<double> doubleArray(12); for (int count = 0; count < intArray.getLength(); ++count) { intArray[count] = count; doubleArray[count] = count + 0.5; } for (int count = intArray.getLength() - 1; count >= 0; --count) { std::cout << intArray[count] << "\t" << doubleArray[count] << '\n'; } return 0; }
运行结果是一样的,但是代码精简很多。learncpp.com上是这样描述使用类模板的好处的:
Template classes are ideal for implementing container classes, because it is highly desirable to have containers work across a wide variety of data types, and templates allow you to do so without duplicating code. Although the syntax is ugly, and the error messages can be cryptic, template classes are truly one of C++’s best and most useful features.
有几点需要注意:
1. 直接将类模板的定义放在一个头文件中就可以了,不需要有cpp文件。
2. 和函数模板一样,类模板中也可以出现固定的数据类型。在learncpp.com上面,这个固定的数据类型被称为class template expression parameter。如下面例子所示:
#include <iostream> template <class T, int size> // size is the expression parameter class StaticArray { private: // The expression parameter controls the size of the array T m_array[size]; public: T* getArray(); T& operator[](int index) { return m_array[index]; } }; // Showing how a function for a class with an expression parameter is defined outside of the class template <class T, int size> T* StaticArray<T, size>::getArray() { return m_array; } int main() { // declare an integer array with room for 12 integers StaticArray<int, 12> intArray; // Fill it up in order, then print it backwards for (int count = 0; count < 12; ++count) { intArray[count] = count; } for (int count = 11; count >= 0; --count) { std::cout << intArray[count] << " "; } std::cout << '\n'; // declare a double buffer with room for 4 doubles StaticArray<double, 4> doubleArray; for (int count = 0; count < 4; ++count) { doubleArray[count] = 4.4 + 0.1*count; } for (int count = 0; count < 4; ++count) { std::cout < doubleArray[count] << ' '; } return 0; }