STL的设计
- 一、简介
- 二、STL容器
- 三、C数组
- 四、用户定义的集合
- 4.1、使用标准集合的typedef
- 4.2、重用标准迭代器
- 4.3、实现自己的迭代器
- 五、总结
一、简介
本文介绍STL的设计方式,以及如何设计自己的组件,使其能够充分利用STL的功能。
STL的设计旨在将算法与数据结构分离。
算法包括:
- 头文件
<algorithm>
中的算法。 - 当我们的需求无法通过标准算法解决时,我们自己编写的算法。
数据包括:
- 标准STL容器,如
std::map
和std::vector
。 - C 数组。
- 用户定义的集合。
- 上述内容的任何子部分。
数据甚至可以从流中获取。
通过“迭代器”接口,已经实现了将算法与数据结构分离的目的。
为了从各种算法的众多优势中受益,数据必须具备迭代器接口。下面介绍了如何为不同类型的数据实现迭代器。
二、STL容器
通过以下方式可以获得迭代器:
begin()
和end()
。rbegin()
和rend()
用于反向迭代器。cbegin()
和cend()
(或者const
容器上的begin()
和end()
)用于常量迭代器。crbegin()
和crend()
(或者const
容器上的rbegin()
和rend()
)用于常量反向迭代器。
三、C数组
对于C数组,指针扮演迭代器的角色。
int myInts[100];
std::for_each(myInts, myInts + 100, doSomething);
严格来说,myInts
不是指针而是数组,但它仍然提供对数组的第一个元素的访问,而myInts + 100
指向“结束后”的地址,符合begin-end
的语义。
因此,C数组可以与算法一起使用,在旧代码中非常有帮助。
需要注意的是,自C++11以来引入了一种新的统一语法,使用std::begin
(和std::end
)自由函数(而不是类方法)。它们可以统一地用于任何具有可以无参数调用的begin(或end)方法的类型,并且也可以用于C数组。
下面的代码示例说明了这种统一性:
int myInts[100];
std::vector<int> vec(100, 0); // 大小为100且初始化为0
std::for_each(std::begin(vec), std::end(vec), doSomething);
std::for_each(std::begin(myInts), std::end(myInts), doSomething);
这使得使用C数组变得更加简单,并且对于通用代码非常方便。
需要注意的是,对于C数组,必须显式地写出std
命名空间,因为它无法使用ADL,但对于vector
可以省略std
命名空间。
四、用户定义的集合
有时会编写自己的集合来满足特定领域的需求。以用户定义的FlowCollection
类为例,该类表示一组财务流量。根据上面所见,为了能够使用算法,它需要提供迭代器。那么,该如何做呢?
4.1、使用标准集合的typedef
每当想编写一个集合时,先考虑一下是否有标准集合可以满足需求。这样可以减少需要编写的代码量。在很多情况下,标准集合就足够了,可以使用typedef
为它加上一个领域名称。例如:
using FlowCollection = std::vector<Flow>;
这样,就可以免费获得所有迭代器以及std::vector
的所有功能,同时还有一个带有领域名称的类型。
4.2、重用标准迭代器
如果集合确实需要领域功能,或者只想要标准容器提供的部分功能,可能需要定义一个包装标准容器的类。在这种情况下,可以使用标准容器的迭代器来实现迭代器:
// 接口
class FlowCollection
{
public:
// ...领域接口...
// 允许访问数据的迭代器
using const_iterator = std::vector<Flow>::const_iterator;
const_iterator begin() const;
const_iterator end() const;
// 允许修改数据的迭代器
using iterator = std::vector<Flow>::iterator;
iterator begin();
iterator end();
// 其他迭代器...
private:
std::vector<Flow> m_flows;
// ...领域数据...
};
// 实现
FlowCollection::iterator FlowCollection::begin()
{
return m_flows.begin();
}
4.3、实现自己的迭代器
如果集合的复杂性超过了前面两种技术的范围,可能需要实现自己的迭代器。这样做更加复杂,超出了本文的范围,并且只有在非常罕见的情况下才需要这样做。
五、总结
这就是STL在当今C++中的地位。如果想要一窥STL在未来的发展趋势(以及如何立即开始使用它),可以看看ranges
的相关内容。