首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

类模板

与函数相似,类也可以被一种或多种类型参数化。容器类就是一个具有这种特性的典型例子,它通常被用于管理某种特定类型的元素。只要使用类模板,你就可以实现容器类,而不需要确定容器中元素的类型。在这一章中,我们使用Stack作为类模板的例子。

3.1  类模板Stack的实现

与函数模板的处理方式一样,我们在同一个头文件中声明和定义类Stack< >(我们将在6.3小节讨论如何把声明和定义放在不同的文件中),如下:

//basics/stack1.hpp

#include <vector>

#include <stdexcept>

template <typename T>

class Stack {

  private:

    std::vector<T> elems;    // 存储元素的容器

  public:

    void push(T const&);     // 压入元素

    void pop();               // 弹出元素

    T top() const;            // 返回栈顶元素

    bool empty() const {     // 返回栈是否为空

        return elems.empty();

    }

};

template <typename T>

void Stack<T>::push (T const& elem)

{

    elems.push_back(elem);    // 把elem的拷贝附加到末尾

}

template<typename T>

void Stack<T>::pop ()

{

    if (elems.empty()) {

        throw std::out_of_range("Stack<>::pop(): empty stack");

    }

    elems.pop_back();         //删除最后一个元素

}

template <typename T>

T Stack<T>::top () const

{

    if (elems.empty()) {

        throw std::out_of_range("Stack<>::top(): empty stack");

    }

    return elems.back();      // 返回最后一个元素的拷贝

}

可以看出,类模板Stack<>是通过C++标准库的类模板vector< >来实现的;因此,我们不需要亲自实现内存管理、拷贝构造函数和赋值运算符;从而可以把精力放在该类模板的接口实现上。

3.1.1  类模板的声明

类模板的声明和函数模板的声明很相似:在声明之前,我们先(用一条语句)声明作为类型参数的标识符;我们继续使用T作为该标识符:

template <typename T>

class Stack {

    ...

};

在此,我们可以再次使用关键字class来代替typename:

template <class T>

class Stack {

...

};

在类模板的内部,T可以像其他任何类型一样,用于声明成员变量和成员函数。在下面的例子中,T被用于声明vector的元素类型,声明push()是一个接收常量T引用为唯一实参的成员函数,声明top()是返回类型为T的成员函数:

template <typename T>

class Stack {

    private:

        std::vector<T> elems;   //存储元素的容器

       

    public:

        Stack();                    //构造函数

        void push(T const &);   //压入元素

        void pop();             //弹出元素

        T top() const;          //返回栈顶元素

};

这个类的类型是Stack<T>,其中T是模板参数。因此,当在声明中需要使用该类的类型时,你必须使用Stack<T>。例如,如果你要声明自己实现的拷贝构造函数和赋值运算符,那么应该这样编写[8]

template <typename T>

class Stack {

    ...

    Stack (Stack<T> const&);                    //拷贝构造函数

    Stack<T>& operator= (Stack<T> const&);  //赋值运算符

    ...

};

然而,当使用类名而不是类的类型时,就应该只用Stack;譬如,当你指定类的名称、类的构造函数、析构函数时,就应该使用Stack。

3.1.2  成员函数的实现

为了定义类模板的成员函数,你必须指定该成员函数是一个函数模板,而且你还需要使用这个类模板的完整类型限定符。因此,类型Stack<T>的成员函数push()的实现如下:

    template <typename T>

    void Stack<T>::push (T const& elem)

    {

        elems.push_back (elem); //把传入实参elem的拷贝                                                           //附加到末端

    }

在上面的例子中,调用了对应vector的push_back()方法,它把传入元素附加到该vector的末端。

请注意:vector的pop_back()方法只是删除末尾的元素,并没有返回该元素;之所以如此是充分考虑了异常安全性,因为要实现“一个绝对异常安全并且返回被删除元素的pop()”是不可能的(Tom Cargill在[CargillExceptionSafety]中首次讨论了这个话题,Sutter在[SutterExceptional]的Item 10也提到这个问题)。然而,如果不考虑异常安全性,我们就可以实现一个返回被删除元素的pop()。事实上,只需要使用T声明一个局部变量,并保证该变量的类型就是vector元素的类型即可;具体如下:

template<typename T>

T Stack<T>::pop()

{

    if (elems.empty() ) {

        throw std::out_of_range(“Stack<>::pop(): empty Stack”);

    }

    T elem = elems.back();      //先保存末端元素的拷贝

    elems.pop_back();           //删除末端元素

    return elem;                    //返回上面保存的元素的拷贝

}

因为当vector为空的时候,它的back()方法(返回末端元素的值)和pop_back()方法(删除末端元素)会具有未加定义的行为,因此我们需要先检查该stack是否为空。如果为空,就抛出std::out_of_range异常。同样,在top()的实现中,我们也是用这种办法来判断对应stack是否为空;top()只是返回栈的顶端[10]元素,并不删除该元素:

template<typename T>

T Stack<T>::top() const

{

    if (elems.empty()) {

        throw std::out_of_range(“Stack::top(); empty Stack”);

    }

    return elems.back();        //返回末端元素的拷贝

}

显然,对于类模板的任何成员函数,你都可以把它实现为内联函数,将它定义于类声明里面。例如:

template <typename T>

class Stack {

    ...

    void push (T const& elem) {

        elems.push_back(elem);  //把传入的elem实参附加到末端

    }

    ...

};

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论