百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

C++ STL 漫谈(c++中的stl到底指的什么)

wptr33 2025-06-04 02:13 6 浏览

标准模板库(Standard Template Library,STL)是惠普实验室开发的一个函数库和类库。它是由Alexander Stepanov、Meng Lee和David R Musser在惠普实验室工作时所开发出来的。

STL是一个模板类库和模板函数库。STL并不仅仅是一个库,它更是一种优秀的思想以及一套约定。

STL包含三大组件:容器、算法和迭代器。容器用于容纳和组织元素;算法针对容器执行操作;迭代器则用于访问容器中的元素。这些都不是什么新东西,许多传统的程序库也都包含类似的组件,并且许多程序库也都是采用模板实现而成。STL的优秀思想体现在:容器与在容器上执行的算法之间无需彼此了解,这种戏法是通过迭代器实现的。

模板类库包含了一些常用数据结构(在STL中叫容器),这些模板类库中通常包含了一个迭代器模板类,用于提供给通常算法来遍历容器内部元素。STL容器只包含了线性结构数据,存储单一元素的容器称为序列容器,存储关联元素(用pair模板类封装的key与value)称为关联容器。容器可以动态增长、可以是顺序存储或链式存储,为查找效率需要,也可以是以红黑树作为底层的数据存储结构或哈希存储。

模板函数库包含了一些适用容器的通用算法。

Stepanov并认为,虽然C++中的继承功能可以表示泛型设计,但终究有个限制。虽然可以在基础类型(superclass)定义算法和接口,但不可能要求所有对象皆是继承这些,而且庞大的继承体系将减低虚拟(virtual)函数的运行效率,这便违反C++所强调的“效率”原则。(C++标准库和STL都是没有所谓基类Object这一语法机制。)

事实上,C++的模板,本身即是一套复杂的宏语言(macro language),宏语言最大的特色为:所有工作在编译时期就已完成。显式的声明函数模板之实例,与直接通过C++的重载功能隐式声明,结果一样,并无很大区别,只是前者加重程序员的负担,使得程序变得累赘。

STL 将“在数据上执行的操作”与“要执行操作的数据分开”,分别以如下概念指代:

容器:数据结构的概念,包含、放置数据的地方(模板类,封装了与容器结合紧密的一些操作容器元素的函数)。

迭代器:在容器中指出一个位置、或成对使用以划定一个区域,用来限定操作所涉及到的数据范围。是数据结构与算法的中间层,是封装在容器模板类内的模板类,迭代器类封装的是外部类(容器类)元素指针,迭代器通常用做算法参数或类似指针用途。

算法:可以在绝大部分容器执行的操作(以适当类别的迭代器为参数)。

1 容器(数据结构)

STL容器是对数据结构的一种抽象,以类模板的方式实现而成。由于具有不同的数据结构,因此不同的容器以不同的方式来组织其元素,以便对存取或操纵进行优化。

标准模板库包含了序列容器(sequence containers)与关系容器(associative containers)。

序列容器包括vector,list,forward_list,deque和array等。

关联容器包括set,multiset,map,multimap,unordered_set,bitset和valarray等。

2 迭代器

迭代器类似于指针(实际上指针就是一种STL迭代器)。像指针一样,迭代器可以指向序列中的一个元素,也可以对其进行解引用(dereference) , 以便获得它所指向的对象的值。我们可以像操纵指针那样操纵迭代器,使其指向序列中不同的元素(其背后中指针操作符的重载)。STL迭代器既可以是预定义的指针,也可以是用户自定义的类类型,当然,这种类型需要重载适当的操作符,以便与预定义指针拥有相同的使用语法。

迭代器是泛化的指针,通过使用迭代器,开发者可以操作数据结构而无需关心其内部实现。根据迭代器的操作方式的不同,迭代器分为输入、输出、前向、双向、随机访问迭代器。

demo code(自定义链表和迭代器):

#include <list>
#include <iostream>
#include <string>
using namespace std;

template <typename T>      // 1 数据组成
struct singleList
{
    singleList<T>* next;
    T data;
    
    singleList()                             // 创建表头
    {
        this->next = NULL;
    }
    
    singleList(T data)                      // 创建节点
    {
        this->data = data;
        this->next = NULL;
    }
    
    singleList(T data, singleList<T>* next) // 创建节点+连接节点的功能
    {
        this->data = data;
        this->next = next;
    }
};

template <typename T>
class List{
public:
    List()  // 构造函数的万金油写法
    {
            // 构造函数作用:构造对象
            // 初始化基本数据成员,描述最初状态
        sizeList = 0;
        listHeadNode = listTailNode = NULL; // 对应STL库list的迭代器begin()和end()返回的指针
    }
    
    void push_back(T data)                  // 基本行为:插入操作
    {
        singleList<T>* newNode = new singleList<T>(data);
        if (listHeadNode == NULL)
            listHeadNode = newNode;
        else
            listTailNode->next = newNode;
        listTailNode = newNode;            // 两种情况的尾节点都指向新节点
        //listTailNode->next=NULL;
    }
	
    void push_front(T data)
    {
        singleList<T>* newNode = new singleList<T>(data);
        if (listHeadNode == NULL)
            listTailNode = newNode;
        else
            newNode->next = listHeadNode;
        listHeadNode = newNode;            // 两种情况的头节点都指向新节点
    }
    //测试行为:遍历结构(打印输出)
    void printList()
    {
        singleList<T>* pMove = listHeadNode;
        while (pMove)
        {
            cout << pMove->data << " ";
            pMove = pMove->next;
        }
        cout << endl;
    }
    singleList<T>* begin()
    {
        return listHeadNode;
    }
    singleList<T>* end()
    {
        return listTailNode->next;
        // 表示链表最后那个位置,NULL的位置
    }
    //万金油行为
    int size() const
    {
        return sizeList;
    }
    bool empty() const
    {
        return sizeList == 0;
    }
public:
    class iterator   // 迭代器是一个类中类,用类的对象来模仿指针,需包含一个对象指针和重载操作符
    {
    public:
                     // 实现begin对iterator对象的初始化
        void operator=(singleList<T>* pMove)
        {
                      // 实质初始化对象中的属性,而不是对象的例子
            this->pMove = pMove;
        }

        singleList<T>* getData()
        {
            return pMove;
        }

        bool operator!=(singleList<T>* pMove)
        {
            return this->pMove != pMove;
        }

        // ++实质是链表的一个移动指针走到一下一个节点
        iterator& operator++()               // 前置++重载
        {
            this->pMove = this->pMove->next; // 链式存储指针的移动
            return (*this);
        }

        T operator*()
        {
            return this->pMove->data;
        }

    protected:

        singleList<T>* pMove;
    };
    protected:
        // 抽象数据成员
        // 遵循的原则:抽象出来的属性能够便于描述行为(成员函数)
        singleList<T>* listHeadNode;
        singleList<T>* listTailNode;
        //万金油属性
        int sizeList;
};

int main()
{
	cout<<"STL中list的使用:"<<endl;
    list<string> mylist;
    mylist.push_back("I");
    mylist.push_back("Love");
    mylist.push_back("you");
    list<string>::iterator listIter;
    for (listIter = mylist.begin(); listIter != mylist.end(); ++listIter)
    {
        cout << *listIter << " ";
    }
    cout << endl;

	cout<<"自定义list的使用:"<<endl;
    List<string> myList;
    myList.push_back("I");
    myList.push_back("Love");
    myList.push_back("you");
    //myList.printList();
    List<string>::iterator myIter;
    //myIter=myList.begin();
    //cout<<myIter.getData()->data<<endl;
    for (myIter = myList.begin(); myIter != myList.end(); ++myIter)
    {
        cout << *myIter << " ";
    }
    cout << endl;
	cin.get();
    return 0;
}
/* 输出
STL中list的使用:
I Love you
自定义list的使用:
I Love you
*/

3 算法

STL提供了一些常见的算法,如排序和搜索等。这些算法与数据结构的实现进行了分离。因此,也可对自定义的数据结构使用这些算法,只需让这些自定义的数据结构拥有算法所预期的迭代器。

STL算法是对函数的一种抽象,采用函数模板实现。大多数STL算法用于处理一个或多个序列的值,其中每一个序列由一对有序的迭代器定义。其中第一个迭代器指向序列的第一个元素,第二个迭代器则指向序列最后一个元素之后的那个位置(而不是最后一个元素)。如果两个迭代器指向同一个位置,那么它们就定义了一个空白序列。

迭代器提供了一种使容器与算法协同工作的机制。一个容器可以生成一对迭代器来指定一个元素序列(可以是全部元素,也可以只是一个子区间),而算法则对该序列进行操作。采用这种方式,容器和算法可以紧密地协作,同时还可以保持彼此不知情。

4 函数对象和适配器

除了容器、算法和迭代器之外,STL还定义了大量的辅助性功能。算法和容器可以采用函数指针和函数对象根据需要进行定制,而这些函数对象又可以通过形形色色的函数对象适配器(function object adapter)进行配接和联合。容器也可以利用容器适配器(container adapter)进行配接,从而将容器的接口修改为栈、队列或优先队列。

4.1 函数对象

狭义的函数对象即重载了操作符()的类的实例,而广义来讲所有可用 x(...) 形式调用的 x 都可称为函数对象、或曰可调用对象。

4.2 适配器

适配器(Adaptor)为一个模板类,用于提供接口映射。

5 约定(convention)

STL对约定(convention)有着很强的依赖。容器和函数对象必须通过一套标准的嵌套类型名字对其自身进行描述。容器和函数对象适配器均要求成员函数具有特定的名字并包含特定的类型信息。算法要求传递给它的迭代器支持特定的操作并能够识别是什么样的操作。当使用或扩展STL时,如果弃约定于不顾,那么同时也就放弃了美梦成真的希望。遵守约定,将拥有简单而轻松的生活。

STL约定并未指明具体的实现细节,但对实现指定了效率方面的约束。此外,由于STL是一个模板库,许多优化和性能调整可以在编译期进行。前面提到的命名和信息约定会对编译期优化起到重要的作用。一般而言,STL的效率可以与专家手写代码的效率相雄美,而明显比普通程序员和程序员小组的手写代码胜出一筹。另外,使用STL可以使代码变得更清晰,更易于维护。

6 萃取器(traits)

有时仅仅知道对象的类型是不够的,通常来说,某些与对象类型有关的信息对于处理对象来说不可或缺。

traits类是一个关于某个类型的信息的集合。然而,与嵌套的容器信息不同的是,traits类独立于它所描述的类型。

有关traits类的一个常见的应用,是在泛型算法和不遵从算法期望的约定的类型之间,放入一个遵从约定的中间层。根据类型的traits编写算法。在一般情况下通常假设存在某种约定。

-End-

相关推荐

C++企业级开发规范指南(c++开发gui)

打造高质量、可维护的C++代码标准一、前言C++作为一门功能强大的系统级编程语言,被广泛应用于操作系统、游戏引擎、高性能服务器、数据库系统等领域。知名互联网公司(如Google、Microsoft、腾...

C++|整型的最值、上溢、下溢、截断、类型提升和转换

整数在计算机内以有限字长表示,当超出最值(有限字长)时,需要截断(溢出,求模)操作。不同字长的整型具有不同的值域,混合运算时,需要类型提升和转换。1整形最值在<limit.h>中有整型的...

C++|漫谈STL细节及内部原理(c++ std stl)

1988年,AlexanderStepanov开始进入惠普的PaloAlto实验室工作,在随后的4年中,他从事的是有关磁盘驱动器方面的工作。直到1992年,由于参加并主持了实验室主任BillWo...

C++11新特性总结 (二)(c++11新特性 pdf)

1.范围for语句C++11引入了一种更为简单的for语句,这种for语句可以很方便的遍历容器或其他序列的所有元素vector<int>vec={1,2,3,4,5,6};f...

C++ STL 漫谈(c++中的stl到底指的什么)

标准模板库(StandardTemplateLibrary,STL)是惠普实验室开发的一个函数库和类库。它是由AlexanderStepanov、MengLee和DavidRMusser在...

C++学习教程_C++语言随到随学_不耽误上班_0基础

C++学习教程0基础学C++也可以,空闲时间学习,不耽误上班.2019年C语言新课程已经上线,随到随学,互动性强,效果好!带你征服C++语言,让所有学过和没有学过C++语言的人,或是正准备学习C++语...

C++遍历vector元素的四种方式(c++ 遍历vector)

vector是相同类型对象的集合,集合中的每个对象有个对应的索引。vector常被称为容器(container)。C++中遍历vector的所有元素是相当常用的操作,这里介绍四种方式。1、通过下标访问...

一起学习c++11——c++11中的新增的容器

c++11新增的容器1:array当时的初衷是希望提供一个在栈上分配的,定长数组,而且可以使用stl中的模板算法。array的用法如下:#include<string>#includ...

C++编程实战基础篇:一维数组应用之投票统计

题目描述班上有N个同学,有五位候选人“A,B,C,D,E”,请所有的同学投票并选举出班长,现在请你编写程序来他们计算候选人的得票总数,每位同学投票将以数字的形式投票“12345”分别代表五位候选人,...

C++20 新特性(6):new表达式也支持数组大小推导

new表达式也支持数组大小推导在C++17标准中,在定义并初始化静态数组时,是可以忽略数组大小,然后通过初始化数据来推导数组的大小。但使用new来定义并初始化动态数组时,并不支持这种自动推导数组大...

C++ 结构体(struct)最全详解(c++结构体用法)

一、定义与声明1.先定义结构体类型再单独进行变量定义structStudent{intCode;charName[20];charSex;intA...

自学 C++ 第 6 课 二维数组找最值

键盘输入一个m×n的二维数组,通过C++编程找出元素中的最大值,并输出其所在的位置坐标。例如,输入一个4×5的二维数组,数组元素分别为{{556623749},{578964563},...

从缺陷中学习C/C++:聊聊 C++ 中常见的内存问题

在写C/C++程序时,一提到内存,大多数人会想到内存泄露。内存泄露是一个令人头疼的问题,尤其在开发大的软件系统时。一个经典的现象是,系统运行了10天、1个月都好好的,忽然有一天宕机了:OOM(Out...

C++开发者都应该使用的十个C++11特性(上)

在C++11新标准中,语言本身和标准库都增加了很多新内容,本文只涉及了一些皮毛。不过我相信这些新特性当中有一些,应该成为所有C++开发者的常规装备。你也许看到过许多类似介绍各种C++11特性的文章。下...

深度解读C/C++指针与数组(c++指针和数组的区别)

指针和数组是密切相关的。事实上,指针和数组在很多情况下是可以互换的。例如,一个指向数组开头的指针,可以通过使用指针的算术运算或数组索引来访问数组。今天我们就来聊一聊数组和指针千丝万缕的关系;一维数组与...