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

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

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

标准模板库(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-

相关推荐

redis的八种使用场景

前言:redis是我们工作开发中,经常要打交道的,下面对redis的使用场景做总结介绍也是对redis举报的功能做梳理。缓存Redis最常见的用途是作为缓存,用于加速应用程序的响应速度。...

基于Redis的3种分布式ID生成策略

在分布式系统设计中,全局唯一ID是一个基础而关键的组件。随着业务规模扩大和系统架构向微服务演进,传统的单机自增ID已无法满足需求。高并发、高可用的分布式ID生成方案成为构建可靠分布式系统的必要条件。R...

基于OpenWrt系统路由器的模式切换与网页设计

摘要:目前商用WiFi路由器已应用到多个领域,商家通过给用户提供一个稳定免费WiFi热点达到吸引客户、提升服务的目标。传统路由器自带的Luci界面提供了工厂模式的Web界面,用户可通过该界面配置路...

这篇文章教你看明白 nginx-ingress 控制器

主机nginx一般nginx做主机反向代理(网关)有以下配置...

如何用redis实现注册中心

一句话总结使用Redis实现注册中心:服务注册...

爱可可老师24小时热门分享(2020.5.10)

No1.看自己以前写的代码是种什么体验?No2.DooM-chip!国外网友SylvainLefebvre自制的无CPU、无操作码、无指令计数器...No3.我认为CS学位可以更好,如...

Apportable:拯救程序员,IOS一秒变安卓

摘要:还在为了跨平台使用cocos2d-x吗,拯救objc程序员的奇葩来了,ApportableSDK:FreeAndroidsupportforcocos2d-iPhone。App...

JAVA实现超买超卖方案汇总,那个最适合你,一篇文章彻底讲透

以下是几种Java实现超买超卖问题的核心解决方案及代码示例,针对高并发场景下的库存扣减问题:方案一:Redis原子操作+Lua脚本(推荐)//使用Redis+Lua保证原子性publicbo...

3月26日更新 快速施法自动施法可独立设置

2016年3月26日DOTA2有一个79.6MB的更新主要是针对自动施法和快速施法的调整本来内容不多不少朋友都有自动施法和快速施法的困扰英文更新日志一些视觉BUG修复就不翻译了主要翻译自动施...

Redis 是如何提供服务的

在刚刚接触Redis的时候,最想要知道的是一个’setnameJhon’命令到达Redis服务器的时候,它是如何返回’OK’的?里面命令处理的流程如何,具体细节怎么样?你一定有问过自己...

lua _G、_VERSION使用

到这里我们已经把lua基础库中的函数介绍完了,除了函数外基础库中还有两个常量,一个是_G,另一个是_VERSION。_G是基础库本身,指向自己,这个变量很有意思,可以无限引用自己,最后得到的还是自己,...

China&#39;s top diplomat to chair third China-Pacific Island countries foreign ministers&#39; meeting

BEIJING,May21(Xinhua)--ChineseForeignMinisterWangYi,alsoamemberofthePoliticalBureau...

移动工作交流工具Lua推出Insights数据分析产品

Lua是一个适用于各种职业人士的移动交流平台,它在今天推出了一项叫做Insights的全新功能。Insights是一个数据平台,客户可以在上面实时看到员工之间的交流情况,并分析这些情况对公司发展的影响...

Redis 7新武器:用Redis Stack实现向量搜索的极限压测

当传统关系型数据库还在为向量相似度搜索的性能挣扎时,Redis7的RedisStack...

Nginx/OpenResty详解,Nginx Lua编程,重定向与内部子请求

重定向与内部子请求Nginx的rewrite指令不仅可以在Nginx内部的server、location之间进行跳转,还可以进行外部链接的重定向。通过ngx_lua模块的Lua函数除了能实现Nginx...