C++泛型编程- 类模板

类模板

类模板是用来生成类的蓝图的,与模板函数不同之处在于。编译器不能为模板推断模板的参数类型,如我们多次已经看到的,为了使用类模板,我们必须在模板名后的尖括号中提供额外信息,用来代替模板参数的模板实参列表

定义类模板

作为一个例子,我们将实现一个类 StrBlob的模板版本,我们将此模板命名为Blob。类似函数模板,类模板以关键字template开始,后跟模板参数列表,在类模板的定义中,我们将模板参数当做替身,代替使用模板时用户需要提供的类型或值。

template<typename T>
class Blob
{
public:
    typedef T value_type;
    typedef typename std::vector::size_type size_type;
    //构造函数
    Blob();
    Blob(std::initializer_list il);
    //Blob中的元素数目
    size_type size() const {return data->size();}
    bool empty()const { return data->empty(); }
    void push_back(const T&& t) {data->push_back(std::move(t)); }
    void push_back(const T& t) {data->push_back(t); }
private:
    std::shared_ptr<std::vector> data;
    void check(size_type i, const std::string& msg) const;

}

我们的模板有一个名为T的模板类型参数,用来表示Blob保存的元素的类型,例如,我们将元素访问操作返回类型定义为T&。用户实例化Blob时,T就会被替换为特定的模板实参类型。

实例化类模板

我们已经多次见到,当使用一个类模板时,我们必须提供额外的信息,我们现在知道这些额外信息就是显式模板实参列表,他们被绑定到模板参数,编译器使用这些模板实参来实例化出特定的类。
例如:

Blob<int> ia;
Blob<int> ia2 = {0, 1, 2, 3, 4};

ia和ia2 会使用相同的特定类型版本的Blob,根据这两个定义,编译器会实例化出一个与下面定义等价的类:

template<>
class Blob<int>
{
public:
    typedef typename std::vector<int>::size_type size_type;
    Blob();
    Blob(std::initializet_list<int> il);
    //...
private:
    std::shared_ptr<std::vector<int>> data;
}

对我们指定的每一种元素类型,编译器都生成不同的一个类,一个类模板的每个实例都形成一个独立的类,类型Blob与任何其他Blob类型都没有关联,也不会对其他任何Blob类型的成员有特殊的访问权限。

在模板作用域中引用模板类型

为了阅读模板类代码,应该记住类模板的名字不是一个类型名,而是用来实例化类型,而一个实例化的类型总是包含模板参数的。可能令人迷惑的是 一个类模板中的代码如果使用了另一个模板,通常不将一个实际类型(或值)的名字用作模板实参数,相反的,我们通常将模板自己的参数当做被使用的模板的实参,例如,我们的data成员使用了两个模板,vector和shared_ptr。我们知道,无论何时使用模板都必须提供模板实参,在本例中,我们提供的模板实参就是Blob的模板参数,因此data的定义如下:

std::shared_ptr<std::vector> data;

它使用了Blob的类型参数来声明data是一个shared_ptr的实例,此shared_ptr指向一个保存类型为T的对象的vector实例,当我们实例化一个特定类型的Blob,例如Blob时。data会成为:

shared_prt<vector<string>>

如果我们实例化Blob,则data会成为shared_ptr

类模板的成员函数

与其他类相同,我们既可以在类模板内部,也可以在类模板外部定义为其定义成员函数,且定义在类模板内的成员函数被隐式声明为内联函数,类模板的成员函数本身是一个成员函数,但是类模板的每个实例都有其自己版本的成员函数,因此,类模板的成员函数具有和模板相同的模板参数,因而定义在模板之外的成员函数就必须以关键字template开始,后接类模板参数列表:

template<typename T>
ret-type Blob<T>::member-name(parm-list)

如上例中的check:

templateT>
void Blob<T>::check(size_type i, const std::string& msg) const
{
    //...    
}

除了类名中的不同之处以及使用了模板参数列表外,此函数与元StrBlob类的check成员函数函数一模一样

Blob构造函数

与其他任何定义在模板外的成员一样,构造函数的定义要以模板参数开始

template<typename T>
Blob::Blob():data(std::make_shared<std::vector>()) {}

这段代码在作用域Blob中定义了名为Blob的成员函数,类似StrBlob的默认构造函数,此构造函数分配一个空vector,我们将类模板自己的类型参数作为vector的模板实参来分配vector

类似的,接受一个initializer_list 参数的构造函数将为其类型参数T作为initializer_list参数的元素类型

template<typename T>
Blob::Blob(std::initializer_list il):
    data(std::make_shared<std::vector>(il)){}

类似默认构造函数,此构造函数分配一个新的vector,并使用参数il初始化本vector,为使用这个构造函数,我们必须传递给它一个initializer_list,其中的元素必须与Blob的元素类型兼容

Blob articles = {"a", "an", "the"};
//构造函数参数类型为std::initializer_list ,列表中的每个字符串字面常量隐式转化为一个string

类模板成员的实例化

默认情况下,一个类模板成员只有当程序用到它时才进行实例化,如果一个成员函数不被使用,就不会被实例化。因此衍生出模板的分离编译问题,后续再谈。

你可能感兴趣的:(C++,class)