类模版


至今已经直接使用过类模版很多次了,那么如何自定义类模版呢?基本上,类模版就只是函数模版的概念延伸,如同函数模版实例化后的是各个不同版本的函数,类模版实例化后的是各个不同的类,更具体来说,是各种不同的类型。

例如vector<int>是个实例化后的类型,vector<char>是实例化后另一个类型,vector<int>vector<char>是两种不同的类型。

因为类模版实例化后,会是不同的类、不同的类型,因此定义类模版时,在返回类型涉及类模版本身时,必须包含模版参数,在::范围解析时也必须包含模版参数。

来看个实例吧!在〈嵌套、局部、匿名类〉中的IntLinkedList,只能用于int的元素,可以将之定义为类模版,适用于各个指定的类型,例如:

#include <iostream>
using namespace std;

template <typename T>
class LinkedList {
    class Node {
    public:
        Node(T value, Node *next) : value(value), next(next) {}
        T value;
        Node *next;
    };

    Node *first = nullptr;

public:
    LinkedList<T>& append(T value);
    T get(int i);
};

template <typename T>
LinkedList<T>& LinkedList<T>::append(T value) {
    Node *node = new Node(value, nullptr);
    if(first == nullptr) {
        this->first = node; 
    }
    else {
        Node *last = this->first;
        while(last->next != nullptr) {
            last = last->next;
        }
        last->next = node;
    }
    return *this;
}

template <typename T>
T LinkedList<T>::get(int i) {
    Node *last = this->first;
    int count = 0;
    while(true) {
        if(count == i) {
            return last->value;
        }
        last = last->next;
        count++;
    }
}

int main() {
    LinkedList<int> intLt;
    intLt.append(1).append(2).append(3);
    cout << intLt.get(1) << endl;

    LinkedList<char> charLt;
    charLt.append('a').append('b').append('c');
    cout << charLt.get(2) << endl;

    return 0;
}

可以留意到范例中,是如何返回类本身类型,以及范围解析::是怎么指定的,对于实现于类之中的成员函数,不用范围解析::,也不用重复定义template模版参数名称。例如:

#include <iostream>
using namespace std;

template <typename T>
class LinkedList {
    class Node {
    public:
        Node(T value, Node *next) : value(value), next(next) {}
        T value;
        Node *next;
    };

    Node *first = nullptr;

public:
    LinkedList<T>& append(T value) {
        Node *node = new Node(value, nullptr);
        if(first == nullptr) {
            this->first = node; 
        }
        else {
            Node *last = this->first;
            while(last->next != nullptr) {
                last = last->next;
            }
            last->next = node;
        }
        return *this;
    }

    T get(int i) {
        Node *last = this->first;
        int count = 0;
        while(true) {
            if(count == i) {
                return last->value;
            }
            last = last->next;
            count++;
        }
    }
};

int main() {
    LinkedList<int> intLt;
    intLt.append(1).append(2).append(3);
    cout << intLt.get(1) << endl;

    LinkedList<char> charLt;
    charLt.append('a').append('b').append('c');
    cout << charLt.get(2) << endl;

    return 0;
}

如果static数据成员是在类外指定,记得范围解析时也得加上类型参数,而使用static成员时,必须实例化,即使实例化时指定的类型与static无关,也是得实例化。例如:

#include <iostream>
using namespace std;

template <typename T>
class Foo {
    static int wat;
public:
    static int wat10(); 
};

template <typename T>
int Foo<T>::wat = 10; 

template<typename T>
int Foo<T>::wat10() {
    return wat * 10;
}

int main() {
    cout << Foo<double>::wat10() << endl;
    return 0;
}

模版类中若要定义friend比较麻烦,因为friend与类之间有耦合关系,因此必须事先做好定义,定义friend时才看得到彼此,例如:

#include <iostream>
using namespace std;

template <typename T>
class Foo;

template <typename T>
void foo(Foo<T> t);

template <typename T>
class Foo {
    T t;
public:
    Foo(T t) :t(t) {}
    friend void foo<T>(Foo<T> t);
};

template <typename T>
void foo(Foo<T> f) {
    cout << f.t << endl;
}

int main() {
    Foo<int> f(10);
    foo(f);

    return 0;
}

实例后的类类型与朋友之间的类型是对应的,例如Foo<int>void foo(Foo<int>)才会是朋友,与void foo(Foo<char>)不会是朋友。


展开阅读全文