类模板(class template)

作者: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;
}

参考资料

  1. <<C++实用教程>> 电子工业出版社 郑阿奇 主编 丁有和 编著
  2. The C++ Tutorial:
    http://www.learncpp.com/cpp-tutorial/133-template-classes/
    http://www.learncpp.com/cpp-tutorial/134-template-expression-parameters/