返回完整目录

2.1 类模板Stack的实现 Implementation of Class Template Stack

正如函数模板,可以如下方式在一个头文件中声明和定义类Stack<>

// basics/stack1.hpp

#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() //返回栈stack是否为空
{
return elems.empty();
}
}; template <typename T>
void Stack<T>::push(T const& elem)
{
elems.push_back(elem); //将elem的拷贝放入elems末尾
} 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(); //返回最后一个元素
}

正如所看到的,该类模板使用C++标准库的vector<>实现,这样便无需实现内存管理、拷贝控制和赋值运算,这样便可以将重心放在类模板的接口上。

2.1.1 声明类模板 Declaration of Class Templates

声明类模板与声明函数模板类似:在声明之前,必须声明一个或多个类型参数的标识符。再一次,T是一个常用的标识符:

template <typename T>
class Stack
{
...
};

此处的关键字typename也可以用class代替:

template <class T>
class Stack
{
...
};

在类模板中,T可以像其他任何类型一样用于声明成员和成员函数(member function)。该例子中,T用于声明成员的类型为T的向量vector,用于声明成员函数push()使用T类型作为参数,用于声明成员函数top()的返回类型。

template <typename T>
class Stack
{
private:
std::vector<T> elems; //元素 public:
void push(T const& elem); //压入元素
void pop(); // 弹出元素
T const& top() const; //返回顶上的元素
bool empty() //返回栈stack是否为空
{
return elems.empty();
}
};

该类的类型为Stack,其中T为模板参数。因此,只要在声明中使用该类的类型,必须使用Stack,除非模板参数可以推断而得。然而,在类模板内,使用没有模板实参的类名意味着将类模板实参作为模板参数(However, inside a class template using the class name not followed by template arguments represents the class with its template parameters as its arguments.)(详见13.2.3节)。

比如,如果必须声明构造函数和赋值运算符,这通常看起来像这样:

template <typename T>
class Stack
{
...
Stack(Stack const&); //拷贝构造
Stack& operator=(Stack const&); //赋值运算符
};

这与如下形式等价:

template <typename T>
class Stack
{
...
Stack(Stack<T> const&); //拷贝赋值
Stack<T>& operator=(Stack<T> const&); //赋值运算符
};

但通常意味着对特殊模板参数的特殊处理,因此第一种形式更好。

然而,在类结构之外需要指定模板参数:

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

注意到在需要类名而不是类的类型的地方,仅仅使用Stack便可以。这特别是在构造函数和析构函数名字的情形中。

与非模板类不同,不能在函数内部或者块作用域(block scope)内声明类模板。通常,模板只能定义在全局作用域(global scope)或者命名空间作用域(namespace scope)或者类声明内(详见12.1节)。

2.1.2 成员函数实现 Implementation of Member Functions

定义类模板的成员函数必须指定这是一个模板且必须使用类模板的完整类型限制。因此,类型Stack的成员函数push()的实现为

template <typename T>
void Stack<T>::push(T const& elem)
{
elems.push_back(elem); //将elem的拷贝放入elems末尾
}

该情况下,成员向量的push_bask()方法被调用,将元素放入向量vector的末尾。

注意到向量vector的pop_back()将移除最后一个元素但不返回,这行为的原因是异常安全(exception safety)。不可能实现一个返回移除元素的完全异常安全的pop()版本(该问题首先由Tom Cargill在[CargilExceptionSafety]中的第10项条款[SutterExceptional]中讨论)。然而,如果忽略该危险,可以实现返回移除的元素的pop()。为实现此功能,简单地使用T来声明类型为元素类型的局部变量:

template <typename T>
T Stack<T>::pop()
{
assert(!elems.empty());
T elem = elems.back(); //保存最后一个元素
elems.pop_back(); //移除最后一个元素
return elem; //返回保存元素的拷贝
}

由于当vector中没有元素时,back()(返回最后一个元素)和pop_back()(移除最后一个元素)都将有未定义的行为,因此需要检查栈是否为空。如果为空,则断言(assert),因为在空的栈上调用pop()是错误的。在top()上也需要这么做,它返回顶上的元素但不移除:

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

当然,对于任何成员函数,也可以将类模板的成员函数在类的声明中实现为inline,比如:

template <typename T>
class Stack
{
...
void push(T const& elem)
{
elems.push_back(elem); //将elem放入末尾
}
};

C++ Templates (2.1 类模板Stack的实现 Implementation of Class Template Stack)的更多相关文章

  1. c++11-17 模板核心知识(二)—— 类模板

    类模板声明.实现与使用 Class Instantiation 使用类模板的部分成员函数 Concept 友元 方式一 方式二 类模板的全特化 类模板的偏特化 多模板参数的偏特化 默认模板参数 Typ ...

  2. C++解析(26):函数模板与类模板

    0.目录 1.函数模板 1.1 函数模板与泛型编程 1.2 多参数函数模板 1.3 函数重载遇上函数模板 2.类模板 2.1 类模板 2.2 多参数类模板与特化 2.3 特化的深度分析 3.小结 1. ...

  3. 初步C++类模板学习笔记

    类模板 实现:在上课时间的定义给它的一个或多个参数,这些参数代表了不同的数据类型.                              -->抽象的类. 在调用类模板时, 指定參数, 由编 ...

  4. C++程序设计方法4:类模板

    类模板 在定义类时也可以将一些类型抽象出来,用模板参数来替换,从而使类更具有通用性.这种类被称为模板类,例如: template <typename T> class A { T data ...

  5. C++ 模板学习 函数模板、类模板、迭代器模板

    使用模板能够极大到使得代码可重用. 记录一下,方便后续使用. 1. 函数模板,支持多种类型参数 #include <stdio.h> #include <math.h> //函 ...

  6. C++ 函数模板与类模板(使用 Qt 开发编译环境)

    注意:本文中代码均使用 Qt 开发编译环境,如有疑问和建议欢迎随时留言. 模板是 C++ 支持参数化程序设计的工具,通过它可以实现参数多态性.所谓参数多态性,就是将程序所处理的对象的类型参数化,使得一 ...

  7. C++入门经典-例9.3-类模板,简单类模板

    1:使用template关键字不但可以定义函数模板,而且可以定义类模板.类模板代表一族类,它是用来描述通用数据类型或处理方法的机制,它使类中的一些数据成员和成员函数的参数或返回值可以取任意数据类型.类 ...

  8. C++ Templates (2.2 使用Stack类模板 Use of Class Template Stack )

    返回完整目录 目录 2.2 使用Stack类模板 Use of Class Template Stack 2.2 使用Stack类模板 Use of Class Template Stack 在C++ ...

  9. C++ Templates (2.3 类模板的局部使用 Partial Usage of Class Templates)

    返回完整目录 目录 2.3 类模板的局部使用 Partial Usage of Class Templates 2.3.1 Concepts 2.3 类模板的局部使用 Partial Usage of ...

随机推荐

  1. js 从目标数组中过滤掉 一个数组元素,

    标题描述的有点僵硬,大概需求是,从目标数组中过滤掉我想要删除的元素集合,这里使用的是遍历+过滤器的组合,很方便,做个笔记! let old = ["AE_CN_SUPER_ECONOMY_G ...

  2. scrapy中选择器用法

    一.Selector选择器介绍 python从网页中提取数据常用以下两种方法: lxml:基于ElementTree的XML解析库(也可以解析HTML),不是python的标准库 BeautifulS ...

  3. PHP ftp_systype() 函数

    定义和用法 ftp_systype() 函数返回 FTP 服务器的系统类型标识符. 如果成功,该函数返回系统类型.如果失败,则返回 FALSE. 语法 ftp_systype(ftp_connecti ...

  4. PDOStatement::fetchColumn

    PDOStatement::fetchColumn — 从结果集中的下一行返回单独的一列.(PHP 5 >= 5.1.0, PECL pdo >= 0.9.0)高佣联盟 www.cgewa ...

  5. luogu CF125E MST Company wqs二分 构造

    LINK:CF125E MST Company 难点在于构造 前面说到了求最小值 可以二分出斜率k然后进行\(Kruskal\) 然后可以得到最小值.\(mx\)为值域. 得到最小值之后还有一个构造问 ...

  6. 7.1 NOI模拟赛 凸包套凸包 floyd 计算几何

    计算几何之所以难学 就是因为太抽象了 不够直观 而且情况很多 很繁琐 甚至有一些东西不清不楚.. 这道题注意到题目中的描述 一个鸽子在两个点所连直线上也算. 通过看题解 发现这个地方并非直线而是线段 ...

  7. 5.15 牛客挑战赛40 E 小V和gcd树 树链剖分 主席树 树状数组 根号分治

    LINK:小V和gcd树 时限是8s 所以当时好多nq的暴力都能跑过. 考虑每次询问暴力 跳父亲 这样是nq的 4e8左右 随便过. 不过每次跳到某个点的时候需要得到边权 如果直接暴力gcd的话 nq ...

  8. ERROR 1054 (42S22): Unknown column 'password' in 'field list'

    解决: update MySQL.user set authentication_string=password('123456') where user='root'; FLUSH PRIVILEG ...

  9. SparkBench安装使用入门

    SparkBench安装以及快速开始 欢迎关注我的GitHub~ 本指南假定已经可以正常使用Spark 2.x,并且可以访问安装它的系统. 系统环境 CentOS 7.7.1908 Ambari-Sp ...

  10. 银弹谷零代码开发V百科|使用技巧:你已经是个成熟的系统了,该学会无网络升级了

    银弹谷零代码开发V百科|使用技巧:你已经是个成熟的系统了,该学会无网络升级了 伴随网络时代的发展,当今越来越多用户家庭的日常生活已经离不开网络.它就像是一张巨大的蛛网,连接起我们每一户人家.虽然网络不 ...