jal*_*alf 59
要理解STL,至少要了解C++的某些方面.我会尽力解释它.结构看似简单.图书馆闪耀的地方在于如何使用它可以简化许多复杂的任务.我会坚持一些非常简单的例子,因为其他任何东西都可能会让那些不懂C++的人感到困惑,也因为我不想写一本小说.;)
首先,一些历史.STL(标准模板库)是单独开发的,然后提交给C++标准委员会考虑,让他们可以选择将其纳入语言.但它并不是作为C++标准的一部分开发的,因此,它的设计风格与C++标准库的其他部分非常不同.如果我记得我的古代历史,它也需要标准委员会了解STL并且习惯它.当他们最初看到它时,他们并不太热衷于它,但过了一会儿,意识到它有多么强大和精心设计.所以它被采用到语言中.这一切都发生在20世纪90年代后期,因为语言接近ISO标准化.
STL的核心是提供您对标准库所期望的最基本功能:存储数据序列的能力以及处理这些序列的能力.
每种其他语言都有其标准库的Collections/Containers部分,包含动态数组(在Java中称为arraylists,在C#中为List,在C++中为向量),链表,字典和其他常见数据结构的实现.
它们通常还提供一些穿越这些结构的机制.(例如,枚举器或迭代器)
STL在C++中提供了相同的功能,但它以异常优雅的方式提供,并带有一些有趣的抽象.
STL完全分为三个独立的组件:
for_each()
允许您将函数应用于序列中的每个元素的函数,或将transform()
函数应用于每个元素的相关函数,以及将结果存储到单独的序列中(非常类似于映射)在函数式语言中运行,或累积(类似于函数式语言中的折叠),还有排序或搜索功能,以及允许您复制整个序列的函数.请注意,这三个区域之间没有重叠.容器存储(并拥有)数据,并生成迭代器.迭代器允许您检查,修改和遍历数据.并且算法在迭代器范围上运行
从概念上讲,迭代器有两个功能.它指向一些数据,它可以在序列中移动(取决于迭代器类型,可以使用不同的移动操作.几乎所有迭代器都可以移动到下一个元素.有些也可以移动到前一个,有些可以向后和向前跳跃任意距离).如果你熟悉C,这听起来很像指针,这并非巧合.迭代器被建模为指针的泛化,实际上,指针也是有效的迭代器.所有STL算法都适用于指针以及"真实"迭代器.
这意味着是数据的任何序列可以通过一对迭代来表示:第一个迭代器指向在序列中的第一个元素,第二个点一个过去的序列的末尾.
这允许在循环中遍历序列的相当简单的语法:
std::vector<int> container;
for (iter it = container.begin(); it != container.end(); ++it)
{
// perform some operations on the iterator (it) or the element it points to (*it)
++(*it); // increment the value the iterator points to
}
Run Code Online (Sandbox Code Playgroud)
或者我们可以将算法应用于序列:
std::sort(container.begin(), container.end());
Run Code Online (Sandbox Code Playgroud)
请注意,sort函数不知道或不关心它正在处理向量.它传递了两个迭代器,它们可以是任何类型.它们可以是指向数组,链表迭代器或任何其他有效迭代器类型的普通指针.
我们可以通过提供我们自己的比较器函数来概括排序函数(任何带有两个值的函数,如果第一个严格小于另一个,则返回true)
// sort in descending order, by passing in a custom comparer which uses greater than instead of less than
bool greater(int lhs, int rhs) { return lhs > rhs; }
std::sort(container.begin(), container.end(), greater);
Run Code Online (Sandbox Code Playgroud)
当然,我们也可以只对矢量的前五个元素进行排序:
std::sort(container.begin(), container.begin()+5);
Run Code Online (Sandbox Code Playgroud)
begin()和end()函数只是从容器中获取迭代器的便捷函数.我们不必直接使用它们.
另一个很好的技巧是流也可以推广到迭代器中.所以让我们从文件中读取所有整数,并将它们复制到一个数组(当然,数组是普通的C类型,因此它们不是正确的容器,也没有迭代器.但指针工作正常)
int arr[1024];
std::ifstream file("something.txt");
// (note, this assumes <= 1024 integers are read)
std::copy(std::istream_iterator<int>(file) // create an iterator pointing to the current position in the file stream
, std::istream_iterator<int>() // and our "end" iterator. When we reach the end of the stream, testing the two iterators for equality will yield true, and so the operation will halt
, arr);
Run Code Online (Sandbox Code Playgroud)
STL的独特之处在于它的灵活性和可扩展性.它可以与C代码完全互操作(指针是合法的迭代器),它可以简单轻松地扩展(如果你愿意,你可以编写自己的迭代器类型.大多数算法都采用比较器的自定义谓词,就像我上面展示的那样,并且您可以定义自己的容器.也就是说,STL的三个支柱中的每一个都可以被覆盖或扩展,因此可以说STL更像是一种设计策略而不是任何东西.即使你使用STL代码也可以编写STL代码你自己的容器,迭代器和算法.并且因为这三个支柱中的每一个都与其他支柱完全分开,所以它们可以比大多数其他语言更容易更换,这三种职责混合在一起并由同一个类共享.算法不知道它所运行的序列存储在哪个容器(如果有的话)中.它只知道它已经传递的迭代器可以被解引用以访问数据本身.容器不必支持所有 标准算法.它必须能够生成一对迭代器,然后所有功能都是免费的.
相比之下,比如Java,每个集合类必须实现自己的搜索,自己的排序,自己的一切.在C++中,我们只需要一个find()实现.它需要两个迭代器和要查找的值,并遍历查找值的序列.因此,它适用于任何容器类型,甚至是我自己定义的容器类型.
STL的另一个显着特点是在使用它时几乎没有性能损失.C++模板在编译时都被替换,产生的代码可以像在C中手动编码所有内容一样进行优化.上面的sort函数会丢失一些性能,因为我将一个函数指针传递给它作为我的自定义比较器,通常不能内联,但如果我们这样定义它可以修复:
struct greater {
bool operator()(int lhs, int rhs) { return lhs > rhs; }
};
std::sort(container.begin(), container.end(), greater());
Run Code Online (Sandbox Code Playgroud)
现在我们不再传递函数指针,而是一个对象.和成员函数(例如操作者())可以被内联.因此,这种排序功能将与您在C中手动编码的任何功能一样高效.
而且,它甚至不必为sort函数添加任何复杂性.实际上,sort恰好有两个重载.一个采用比较器功能,一个不采用比较器功能.
sort函数不需要知道它是传递函数指针还是对象.只要语法"X(a,b)"有效,其中X是作为比较器传递的值,而a,b是要比较的元素,sort函数的相同实现将起作用.并且因为我的greater
对象重载了operator(),所以该语法对于此对象和我们之前传递的函数指针都是有效的.STL代码通过利用这样的技巧免费获得许多功能.由于C++模板的工作方式,函数的相同实现适用于非常不同的参数类型.
STL是标准模板库.它是C++标准库的子集.
STL提供有用算法和容器的通用实现.
容器提供了在程序中存储数据的任何简单方法,然后查找,排序和执行对该数据的其他计算.