第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

时间: 2024-08-04 01:41:03

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

《Programming WPF》翻译 第9章 2.选择一个基类

原文:<Programming WPF>翻译 第9章 2.选择一个基类 WPF提供了很多类,当创建一个自定义元素时,你可以从这些类中派生.图9-1显示了一组可能作为类--可能是合适的基类,并且说明了他们之间的继承关系.注意到,这决不是完整的继承关系图,只是简单的显示了一些你应该考虑的可能的基类. 无论你选择了哪一个基类,你的元素都会直接或间接地从FrameworkElement派生.这将提供routing事件,高级属性处理,动画,数据绑定,外观上的支持,样式,以及逻辑树的集成. 派生于Fram

《python解释器源码剖析》第13章--python虚拟机中的类机制

13.0 序 这一章我们就来看看python中类是怎么实现的,我们知道C不是一个面向对象语言,而python却是一个面向对象的语言,那么在python的底层,是如何使用C来支持python实现面向对象的功能呢?带着这些疑问,我们下面开始剖析python中类的实现机制.另外,在python2中存在着经典类(classic class)和新式类(new style class),但是到Python3中,经典类已经消失了.并且python2官网都快不维护了,因此我们这一章只会介绍新式类. 13.1 p

C++ 类模板三(类模版中的static关键字)

//类模版中的static关键字 #include<iostream> using namespace std; /* 类模板本质上是c++编译器根据类型参数创建了不同的类, c++编译器在利用类模板生成类的时候会为每个类生成一个static变量 那么对于类中的static关键字就非常好理解了 static关键字修饰的变量是属于类的 同一个类的对象共享类的static静态变量 类模板中的static修饰的变量数据类型必须是确定的 不可以是类型参数 因为静态变量在类对象之前初始化 这时候还没有通

C++ Primer 学习笔记_81_模板与泛型编程 --类模板成员[续1]

模板与泛型编程 --类模板成员[续1] 二.非类型形参的模板实参 template <int hi,int wid> class Screen { public: Screen():screen(hi * wid,'#'), cursor(hi * wid),height(hi),width(wid) {} //.. private: std::string screen; std::string::size_type cursor; std::string::size_type height

C++ Primer 学习笔记_82_模板与泛型编程 --类模板成员[续2]

模板与泛型编程 --类模板成员[续2] 六.完整的Queue类 Queue的完整定义: template <typename Type> class Queue; template <typename Type> ostream &operator<<(ostream &,const Queue<Type> &); template <typename Type> class QueueItem { friend clas

C++模板学习:函数模板、结构体模板、类模板

C++模板:函数.结构体.类 模板实现 1.前言:(知道有模板这回事的童鞋请忽视) 普通函数.函数重载.模板函数 认识. //学过c的童鞋们一定都写过函数sum吧,当时是这样写的: int sum(int a,int b) { return a+b; } //实现了整数的相加 //如果再想同时实现小数的相加,就再多写个小数的相加.普通实现我就不写了,知道函数重载的童鞋们会这样写: int sum(int a,int b) {//第一个function return a+b;} double su

修改tt模板让ADO.NET C# POCO Entity Generator With WCF Support 生成的实体类继承自定义基类

折腾几天记载一下,由于项目实际需要,从edmx生成的实体类能自动继承自定义的基类,这个基类不是从edmx文件中添加的Entityobject. 利用ADO.NET C# POCO Entity Generator With WCF Support生成的tt文件(比如model.tt)中找到 partial class partial class 修改tt模板让ADO.NET C# POCO Entity Generator With WCF Support 生成的实体类继承自定义基类

【C++】智能指针类和OpenCV的Ptr模板类

智能指针类 引用计数 智能指针(smart pointer)的一种通用实现技术是使用引用计数(reference count).智能指针类将一个计数器与类指向的对象相关联,引用计数跟踪该类有多少个对象的指针指向同一对象.引用计数为0时,删除对象. 其基本使用规则是: 每次创建类的新对象时,初始化指针并将引用计数置为1.当对象作为另一对象的副本而创建时,复制构造函数复制指针并增加与之相应的引用计数的值.对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数的值(如果引用计数减至0,则删除对

《Effective C++》:条款43:学习处理模板化基类内的名称

[toc] 模板化的类作为基类时,有哪些要注意的地方.以一个例子说明,假设现在编写一个发送信息到不同公司的程序,信息要么译成密码,要么就是原始文字,在编译期间来决定哪一家公司发送至哪一家公司,采用template手法: class CompanyA{ public: void sendCleartext(const std::string& msg); void sendEncryted(const std::string& msg); -- }; class CompanyB{ publ

[MVC学习笔记]2.使用T4模板生成其他类的具体实现

      在前篇中我们已经将User类中的代码做了具体的实现,但仍然有多个实体类未实现,以后可能还会增加新的数据表,数据表结构也有可能发生变化,所以我们使用T4模板来完成类的生成,这样就算数据库表发生了改变,也会自动根据改变后的实体对类进行重新生成. 下面是数据访问层的T4模板文件Dal.tt <#@ template language="C#" debug="false" hostspecific="true"#> <#@