第2章 类模板:2.1 类模板Stack的实现

Chapter 2: Class Templates

第2章 类模板

Similar to functions, classes can also be parameterized with one or more types. Container classes, which are used to manage elements of a certain type, are a typical example of this feature. By using class templates, you can implement such container classes while the element type is still open. In this chapter we use a stack as an example of a class template.

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

 

2.1 Implementation of Class Template Stack

2.1 类模板Stack的实现

 

As we did with function templates, we declare and define class Stack<> in a header file as follows:

与函数模板的处理方式一样,我们在同一个头文件中声明和定义Stack<>,如下:

#include <vector>
#include <cassert>

template<typename T>
class Stack {
private:
    std::vector<T> elems; //元素
public:
    void push(T const& elem); //压入元素
    void pop(); // 弹出元素
    T const& 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()
{
    assert(!elems.empty());
    elems.pop_back(); //删除最后一个元素
}

template<typename T>
T const& Stack<T>::top() const
{
    assert(!elems.empty());
    return elems.back(); // 返回最后一个元素的拷贝
}

As you can see, the class template is implemented by using a class template of the C++ standard library: vector<>. As a result, we don’t have to implement memory management, copy constructor, and assignment operator, so we can concentrate on the interface of this class template.

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

 

2.1.1 Declaration of Class Templates

2.1.1 类模板的声明

Declaring class templates is similar to declaring function templates: Before the declaration, you have to declare one or multiple identifiers as a type parameter(s).

类模板的声明和函数模板的声明很相似:在声明之前,我们需要先声明一个或多个类型参数的标识符。

 

Again, T is usually used as an identifier:

同样,T通常作为一个标识符:

template<typename T>
class Stack {
    …
};

Here again, the keyword class can be used instead of typename:

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

template<class T>
class Stack {
    …
};

Inside the class template, T can be used just like any other type to declare members and member functions. In this example, T is used to declare the type of the elements as vector of Ts, to declare push() as a member function that uses a T as an argument, and to declare top() as a function that returns a T:

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

template<typename T>
class Stack {
private:
    std::vector<T> elems; // 元素
public:
    void push(T const& elem); // 压入元素
    void pop(); // 弹出元素
    T const& top() const; //返回栈顶元素

    bool empty() const { //判断栈是否为空
        return elems.empty();
    }
};

The type of this class is Stack<T>, with T being a template parameter. Thus, you have to use Stack<T> whenever you use the type of this class in a declaration except in cases where the template arguments can be deduced. However, inside a class template using the class name not followed by template arguments represents the class with its template parameters as its arguments (see Section 13.2.3 on page 221 for details).

这个类的类型是Stack<T>,其中的T是模板参数。因此,当在声明中需要使用该类的类型时,除非可以推导出模板参数,否则就必须使用Stack<T>。但是,在类模板的内部使用类名,而不是带有模板参数的类型名来表示该类(有关详细信息,请参阅第221页13.2.3节)。

If, for example, you have to declare your own copy constructor and assignment operator, it typically looks like this:

例如,如果你想声明自己实现的拷贝构造函数和赋值运算符,那么应该这样编写:

template<typename T>
class Stack {
    …

    Stack(Stack const&); //拷贝构造函数
    Stack& operator= (Stack const&); // 赋值运算符

    …
};

which is formally equivalent to:

在形式上等价于:

template<typename T>
class Stack {
    …

    Stack(Stack<T> const&); // copy constructor
    Stack<T>& operator= (Stack<T> const&); // assignment operator

    …
};

but usually the <T> signals special handling of special template parameters, so it’s usually better to use the first form.

但,经常<T>表示对特殊模板参数进行了特殊处理,因此,通常最好使用第一种形式。

 

However, outside the class structure you’d need:

但是,在类结构体的外部,你必须:

template<typename T>
bool operator== (Stack<T> const& lhs, Stack<T> const& rhs);

Note that in places where the name and not the type of the class is required, only Stack may be used. This is especially the case when you specify the name of constructors (not their arguments) and the destructor.

注意,在需要类的名称而不是类的类型的地方,只能使用Stack。特别是在指定构造函数(不是其参数)和析构函数的名称时。

 

Note also that, unlike nontemplate classes, you can’t declare or define class templates inside functions or block scope. In general, templates can only be defined in global/namespace scope or inside class declarations (see Section 12.1 on page 177 for details).

还要注意,与非模板类不同,不能在函数或块作用域内声明和和定义类模板。通常,模板只能在全局/命名空间或类内部声明(有关详细信息,请参阅177页12.1节)。

 

2.1.2 Implementation of Member Functions

2.1.2 成员函数的实现

 

To define a member function of a class template, you have to specify that it is a template, and you have to use the full type qualification of the class template. Thus, the implementation of the member function push() for type Stack<T> looks like this:

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

template<typename T>
void Stack<T>::push(T const& elem)
{
    elems.push_back(elem); // 抟贝elem并添加到末尾
}

In this case, push_back() of the element vector is called, which appends the element at the end of the vector.

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

 

Note that pop_back() of a vector removes the last element but doesn’t return it. The reason for this behavior is exception safety. It is impossible to implement a completely exception-safe version of pop() that returns the removed element (this topic was first discussed by Tom Cargill in [CargillExceptionSafety] and is discussed as Item 10 in [SutterExceptional]). However, ignoring this danger, we could implement a pop() that returns the element just removed. To do this, we simply use T to declare a local variable of the element type:

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

template<typename T>
T Stack<T>::pop()
{
    assert(!elems.empty());
    T elem = elems.back(); // save copy of last element
    elems.pop_back(); // remove last element
    return elem; // return copy of saved element
}

Because back() (which returns the last element) and pop_back() (which removes the last element) have undefined behavior when there is no element in the vector, we decided to check whether the stack is empty. If it is empty, we assert, because it is a usage error to call pop() on an empty stack. This is also done in top(), which returns but does not remove the top element, on attempts to remove a nonexistent top element:

因为当vector为空的时候,它的back()方法(返回末端元素的值)和pop_back()方法(删除末端元素)会出现未定义行为,因此我们需要先检查该stack是否为空。如果为空,我们就assert,因为在空的stack上调用pop()是一个错误的用法。尝试删除不存在的栈顶元素时,也可以在top()中执行同样的操作。top()返回但不删除栈顶元素。

template<typename T>
T const& Stack<T>::top() const
{
    assert(!elems.empty());
    return elems.back(); // 返回最后一个元素的拷贝
}

Of course, as for any member function, you can also implement member functions of class templates as an inline function inside the class declaration. For example:

当然,对于类模板的任何成员函数,你也可以在类声明中将其实现为内联函数。例如:

template<typename T>
class Stack {
    …
    void push(T const& elem) {
        elems.push_back(elem); // append copy of passed elem
    }
    …
};

原文链接: https://www.cnblogs.com/5iedu/p/12696842.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    第2章 类模板:2.1 类模板Stack的实现

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/392650

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月3日 下午2:54
下一篇 2023年4月3日 下午2:54

相关推荐