C++ Standard Template Library STL(undone)

目录

1. C++标准模版库(Standard Template Library STL)
2. C++ STL容器
3. C++ STL 顺序性容器
4. C++ STL 关联式容器
5. C++ STL 容器适配器
6. C++ STL算法
7. C++ STL边界限制

 

1. C++标准模版库(STL)

STL就是Standard Template Library,标准模板库。从根本上说

1. STL是一些"容器"集合
2. STL也是算法和其他一些组件的集合
3. 这里的"容器""算法"的集合指的是世界上很多聪明人很多年的杰作,使用C++的STL模版库,我们可以像使用C#那样简单的进行面向对象的编程

STL的目的是标准化组件,这样我们就不用重新开发它们了。你可以仅仅使用这些现成的组件。STL现在是C++的一部分,因此不用额外安装什麽。它被内建在你的编译器之内
0X1: STL容器的特点

1. STL容器可以保存
    1) 对象
    2) 内建对象
    3) 类对象
2. STL会安全的保存对象
3. STL定义了我们能够操作的这个对象的接口
4. STL算法是标准算法,我们可以把它们应用在那些容器中的对象上。这些算法都有很著名的执行特性。它们可以
    1) 给对象排序,
    2) 删除对象
    3) 给对象记数
    4) 比较对象
    5) 找出特殊的对象
    6) 把对象合并到另一个容器中
    7) ...
5. STL iterator
STL iterator就像是容器中指向对象的指针。STL的算法使用iterator在容器上进行操作
6. Iterator设置算法的边界、容器的长度、和其他一些事情
    1) 有些iterator仅让算法读元素
    2) 有一些让算法写元素
    3) 有一些则两者都行
7. Iterator也决定在容器中处理的方向 
    1) 通过调用容器的成员函数begin()来得到一个指向一个容器起始位置的iterator
    2) 调用一个容器的 end() 函数来得到过去的最后一个值 

概括地说,STL所有的东西由容器、算法、和允许算法工作在容器中的元素上的iterator这3部分组成。算法以合适、标准的方法操作对象,并可通过iterator得到容器精确的长度。一旦做了这些,它们就在也不会"跑出边界"。还有一些其他的对这些核心组件类型有功能性增强的组件,例如函数对象。我们将会逐一学习

Relevant Link:

http://docs.huihoo.com/gnu/linux/stl.html
http://segmentfault.com/blog/simbest/1190000000325549

 

2. C++ STL容器

0x1: 容器简介

在面向对象程序中,大多引入了容器的概念。容器实质上是一组相同类型对象的集合,但它不仅仅是数组那么简单,它实现了比数组更复杂的数据结构,能够实现更复杂的功能。C++标准模版库里提供了10种通用的容器,它基本可以解决程序中遇到的大部分问题

容器另一个好处就是可以自行扩展,解决问题是我们不知道需要存储多少个对象,数组在这方面是个欠缺。容器可以为你申请内存、释放内存,并且使用最优的算法来执行你的命令,简单来说,使用STL容器不需要考虑申请空间浪费、或者空间不够的上溢出问题

0x2: 容器的分类

1. 顺序性容器
各元素之间有顺序关系的线性表,是一种线性关系的有序集群,顺序容器中的元素均有固定的位置,除非用删除和插入来改变它的位置,这个位置和元素本身无关,和操作时间和地点有关
    1) vector: 从后面快速的插入与删除,直接访问任何元素
    2) deque: 从前面或后面快速的插入与删除,直接访问任何元素
    3) list: 双联表,从任何位置插入和删除,不能直接访问任意元素

2. 关联式容器
非线性的树结构(二叉树结构),各元素之间没有严格的物理顺序。关联容器提供了根据元素特点排序的功能,迭代器根据元素特点"顺序"(这里的顺序是一个数学概念上的顺序,指满足一定的"关系")的取元素
    1) set: 快速查找,不允许重复值
    2) multiset: 快速查找,允许重复值
    3) map: 一对多映射,基于关键字快速查找,不允许重复值
    4) multimap: 一对多映射,基于关键字快速查找,允许重复值

3. 容器适配器
让一种已存在的容器类型采用另一种不同抽象类型的工作方式来实现的一种机制。实质上仅发生了接口转换
    1) stack: 后进先出
    2) queue: 先进先出
    3) priority_queue: 最高优先级第一个出列

Relevant Link:

http://blog.csdn.net/guoshenglong11/article/details/9469881

 

3. C++ STL 顺序性容器

0x1: vector

我们可以把它看成动态数组,创建一个vector后,它会在内存中分配一块连续的区域存储数据,初始空间可以指定也可以由vector决定(capacity()函数返回值)
当数据存储空间超过分配空间,vector会重新分配一块内存区域,这样的分配很耗时,动作如下

1. vector申请一块更大内存区域 
2. 将原来的数据拷贝到新的内存区域 
3. 销毁原来内存块数据
4. 释放原来内存空间

所以,如果vector保存的数据量很大,这样的操作会导致很糟糕的性能(频繁地申请和释放内存是一个很费时的操作)。只有在预知空间大小的情况下,vector的性能才是最好的

Example(从末尾位置顺序存取)

// vector::begin/end
#include <iostream>
#include <vector>

int main ()
{
  std::vector<int> myvector;
  for (int i=1; i<=5; i++)
  {
      myvector.push_back(i);
  }

  std::cout << "myvector contains:";
  for (std::vector<int>::iterator it = myvector.begin() ; it != myvector.end(); ++it)
  {
      std::cout << ' ' << *it;
  }
  std::cout << '\n';

  return 0;
}

Example(任意位置随机存取)

// vector::at
#include <iostream>
#include <vector>

int main ()
{
  std::vector<int> myvector (10);   // 10 zero-initialized ints

  // assign some values:
  for (unsigned i=0; i<myvector.size(); i++)
    myvector.at(i)=i;

  std::cout << "myvector contains:";
  for (unsigned i=0; i<myvector.size(); i++)
    std::cout << ' ' << myvector.at(i);
  std::cout << '\n';

  return 0;
}

Relevant Link:

http://www.cplusplus.com/reference/vector/vector/
http://www.cplusplus.com/reference/vector/vector/begin/
http://www.cplusplus.com/reference/vector/vector/at/

0x2: List

双向链表List结构,指针将所有元素链接起来。根据其结构特点(寻址慢是链表的特点),List检索性能不好,需要顺序查找,检索时间与目标元素位置成正比。但是它可以对快速的删除和插入(链表的插入和删除不需要移动其他的元素)

Example(两头位置顺序存取)

// list::pop_back
#include <iostream>
#include <list>

int main ()
{
    std::list<int> mylist;
    int sum (0);
    //从头部顺序存储
    mylist.push_front(500);
    mylist.push_front(400);
    mylist.push_front(300);
    mylist.push_front(200);
    mylist.push_front(100);

    mylist.push_back(600);
    mylist.push_back(700);
    mylist.push_back(800);
    mylist.push_back(900);

    while (!mylist.empty())
    {
        sum = mylist.front();
        mylist.pop_front();
        std::cout << "The elements of current item is: " << sum << '\n';
    }

    std::cout << "-----------------------" << '\n';

    //从尾部顺序存储
    mylist.push_front(500);
    mylist.push_front(400);
    mylist.push_front(300);
    mylist.push_front(200);
    mylist.push_front(100);

    mylist.push_back(600);
    mylist.push_back(700);
    mylist.push_back(800);
    mylist.push_back(900);

    while (!mylist.empty())
    {
        sum = mylist.back();
        mylist.pop_back();
        std::cout << "The elements of current item is: " << sum << '\n';
    }

    return 0;
}

Example(任意位置顺序插入)

// inserting into a list
#include <iostream>
#include <list>
#include <vector>

int main ()
{
    std::list<int> mylist;
    std::list<int>::iterator it;

    // set some initial values:
    for (int i=1; i<=5; ++i)
    {
        mylist.push_back(i); // 1 2 3 4 5
    }
    it = mylist.begin();
    ++it;       // it points now to number 2           ^

    mylist.insert (it,10);                        // 1 10 2 3 4 5

    // "it" still points to number 2                      ^
    mylist.insert (it,2,20);                      // 1 10 20 20 2 3 4 5

    --it;       // it points now to the second 20            ^

    std::vector<int> myvector (2,30);
    mylist.insert (it,myvector.begin(),myvector.end());
                                        // 1 10 20 30 30 20 2 3 4 5
                                        //               ^
    std::cout << "mylist contains:";
    for (it=mylist.begin(); it!=mylist.end(); ++it)
    {
        std::cout << ' ' << *it;
    }

    std::cout << '\n';

    return 0;
}

Relevant Link:

http://docs.huihoo.com/gnu/linux/stl.html
http://www.cplusplus.com/reference/list/list/insert/

0x3: deque

双端队列deque是一种可以对序列两端插入和删除的容器,可以快速的随即查找。它不像vector把所有元素存储在同一内存块,而是采用多个连续的存储块,并且一个映射结构中保存对这些块和元素顺序的跟踪。deque是vector和List优缺点的结合,它是处于两者之间的一种容器。支持[]及vector.at(),性能没有vector好

Example(从两头位置顺序存取)

// list::pop_back
#include <iostream>
#include <deque>

int main ()
{
    std::deque<int> mydeque;
    int sum (0);
    //从头部顺序存储
    mydeque.push_front(500);
    mydeque.push_front(400);
    mydeque.push_front(300);
    mydeque.push_front(200);
    mydeque.push_front(100);

    mydeque.push_back(600);
    mydeque.push_back(700);
    mydeque.push_back(800);
    mydeque.push_back(900);

    while (!mydeque.empty())
    {
        sum = mydeque.front();
        mydeque.pop_front();
        std::cout << "The elements of current item is: " << sum << '\n';
    }

    std::cout << "-----------------------" << '\n';

    //从尾部顺序存储
    mydeque.push_front(500);
    mydeque.push_front(400);
    mydeque.push_front(300);
    mydeque.push_front(200);
    mydeque.push_front(100);

    mydeque.push_back(600);
    mydeque.push_back(700);
    mydeque.push_back(800);
    mydeque.push_back(900);

    while (!mydeque.empty())
    {
        sum = mydeque.back();
        mydeque.pop_back();
        std::cout << "The elements of current item is: " << sum << '\n';
    }

    return 0;
}

Example(任意位置随机存取)

// deque::at
#include <iostream>
#include <deque>

int main ()
{
    std::deque<unsigned> mydeque (10);   // 10 zero-initialized unsigneds

    // assign some values:
    for (unsigned i=0; i<mydeque.size(); i++)
    {
        mydeque.at(i)=i;
    }
    std::cout << "mydeque contains:";
    for (unsigned i=0; i<mydeque.size(); i++)
    {
        std::cout << ' ' << mydeque.at(i);
    }
    std::cout << '\n';
    return 0;
}

Relevant Link:

http://www.cplusplus.com/reference/deque/deque/at/
http://www.cplusplus.com/reference/deque/deque/?kw=deque

0x4: vector、deque、list的优缺点比较

1. vector是一段连续的内存块,而deque是多个连续的内存块,list是所有数据元素分开保存,可以是任何两个元素没有连续
2. vector的查询性能最好,并且在末端增加数据也很好,除非它重新申请内存段;适合高效地随机存储。
3. list是一个链表,任何一个元素都可以是不连续的,但它都有两个指向上一元素和下一元素的指针。所以它对插入、删除元素性能是最好的,而查询性能非常差;适合大量地插入和删除操作而不关心随机存取的需求
4. deque是介于两者之间,它兼顾了数组和链表的优点,它是分块的链表和多个数组的联合。所以它有被list 好的查询性能,有被vector好的插入、删除性能。 如果你需要随即存取又关心两端数据的插入和删除,那么deque是最佳之

这是一种数据结构的平衡选择

 

4. C++ STL 关联式容器

0x1: set

set又称集合,实际上就是一组元素的集合,但其中所包含的元素的值是唯一的,且是按一定顺序排列的,集合中的每个元素被称作集合中的实例。因为其内部是通过链表的方式来组织,所以在插入的时候比vector快,但在查找和末尾添加上被vector慢

Example(自动排序)

// set::begin/end
#include <iostream>
#include <set>

int main ()
{
    int myints[] = {75,23,65,42,13};
    std::set<int> myset (myints,myints+5);

    std::cout << "myset contains:";
    for (std::set<int>::iterator it=myset.begin(); it!=myset.end(); ++it)
    {
        std::cout << ' ' << *it;
    }
    std::cout << '\n';

    return 0;
}

Example(不允许重复值)

// set::insert (C++98)
#include <iostream>
#include <set>

int main ()
{
    std::set<int> myset;
    std::set<int>::iterator it;
    std::pair<std::set<int>::iterator,bool> ret;

    // set some initial values:
    for (int i=1; i<=5; ++i)
    {
        myset.insert(i*10);    // set: 10 20 30 40 50
    }
    ret = myset.insert(20);               // no new element inserted

    if (ret.second==false)
    {
        it=ret.first;  // "it" now points to element 20
    }

    myset.insert (it,25);                 // max efficiency inserting
    myset.insert (it,24);                 // max efficiency inserting
    myset.insert (it,26);                 // no max efficiency inserting

    int myints[]= {5,10,15};              // 10 already in set, not inserted
    myset.insert (myints,myints+3);

    std::cout << "myset contains:";
    for (it=myset.begin(); it!=myset.end(); ++it)
    {
        std::cout << ' ' << *it;
    }
    std::cout << '\n';

    return 0;
}

Relevant Link:

http://www.cplusplus.com/reference/set/set/?kw=set
http://www.cplusplus.com/reference/set/set/begin/
http://www.cplusplus.com/reference/set/set/insert/

0x2: multiset

multiset是多重集合,其实现方式和set是相似的,只是它不要求集合中的元素是唯一的,也就是说集合中的同一个元素可以出现多次

0x3: map

map提供一种"键-值"关系的一对一的数据存储能力。其"键"在容器中不可重复,且按一定顺序排列
(其实我们可以将set也看成是一种键-值关系的存储,只是它只有键没有值。它是map的一种特殊形式)
由于map其是按链表的方式存储,它也继承了链表的优缺点

Example(按键值进行排序)

// map::begin/end
#include <iostream>
#include <map>

int main ()
{
    std::map<char,int> mymap;
    std::map<char,int>::iterator it;

    mymap['b'] = 100;
    mymap['a'] = 200;
    mymap['c'] = 300;

    // show content:
    for (std::map<char,int>::iterator it=mymap.begin(); it!=mymap.end(); ++it)
    {
        std::cout << it->first << " => " << it->second << '\n';
    }
    return 0;
}

Example(自定义排序函数1)

/*
优先按照prob进行降序排序
如果prob相等,则按照str的字符顺序进行降序排序
*/
#include <iostream>
#include <set>
#include <string>

using namespace std;

struct Word
{
    string str;
    int    prob;
    Word(string s, int i):str(s),prob(i){};
    Word(){};
};

struct MapCmp
{
    bool operator()(const Word s1, const Word s2) const
    {
        if( s1.prob != s2.prob )
        {
            return s1.prob > s2.prob;
        }
        return s1.str.compare(s2.str) > 0;
    }
};

typedef multiset<Word, MapCmp> Mapwords;
Mapwords words;

int main()
{
    words.insert(Word("a",1));
    words.insert(Word("b",1));
    words.insert(Word("c",2));
    words.insert(Word("d",3));
    words.insert(Word("e",3));
    words.insert(Word("f",-2));

    Mapwords::iterator mbegin;
    Mapwords::iterator mend = words.end();
    for( mbegin = words.begin(); mbegin != mend; ++mbegin)
    {
        cout << mbegin->str << '\t' << mbegin->prob << endl;
    }

    return 0;
}

Example(自定义排序函数2)

Relevant Link:

http://www.cplusplus.com/reference/map/map/begin/
http://blog.sina.com.cn/s/blog_5674da3201009mq2.html

0x4: multimap

multimap和map的原理基本相似,它允许"键"在容器中可以不唯一

 

5. C++ STL 容器适配器

适配器是容器的接口,它本身不能直接保存元素,它保存元素的机制是调用另一种顺序容器去实现,即可以把适配器看作"它保存一个容器,这个容器再保存所有元素"。适配器起到一个转换接口的作用。STL中提供的三种适配器可以由某一种顺序容器去实现

//默认情况
1. stack基于deque器实现
栈stack的特点是后进先出,所以它关联的基本容器可以是任意一种顺序容器,因为这些容器类型结构都可以提供栈的操作要求,它们都提供了push_back 、pop_back 和back 操作

2. queue基于deque器实现
队列queue的特点是先进先出,适配器要求其关联的基础容器必须提供pop_front作,因此其不能建立在vector容器上

3. priority_queue基于vector容器实现
优先级队列priority_queue适配器要求提供随机访问功能,因此不能建立在list容器上

当然在创建一个适配器时也可以指定具体的实现容器,创建适配器时在第二个参数上指定具体的顺序容器可以覆盖适配器的默认实现

0x1: stack

Example(Last in First out,LIFO)

// stack::push/pop
#include <iostream>       // std::cout
#include <stack>          // std::stack

int main ()
{
    std::stack<int> mystack;

    for (int i=0; i<5; ++i)
    {
        mystack.push(i);
    }

    std::cout << "Popping out elements...";
    while (!mystack.empty())
    {
        std::cout << ' ' << mystack.top();
        mystack.pop();
    }
    std::cout << '\n';

    return 0;
}

Relevant Link:

http://www.cplusplus.com/reference/stack/stack/?kw=stack
http://www.cplusplus.com/reference/stack/stack/push/

0x2: queue

Example(First In First Out、FIFO)

// queue::push/pop
#include <iostream>       // std::cin, std::cout
#include <queue>          // std::queue

int main ()
{
    std::queue<int> myqueue;
    int myint;

    std::cout << "Please enter some integers (enter 0 to end):\n";

    for (int i=0; i<5; ++i)
    {
        myqueue.push(i);
    }

    std::cout << "myqueue contains: ";
    while (!myqueue.empty())
    {
        std::cout << ' ' << myqueue.front();
        myqueue.pop();
    }
    std::cout << '\n';

    return 0;
}

Relevant Link:

http://www.cplusplus.com/reference/queue/queue/?kw=queue
http://www.cplusplus.com/reference/queue/queue/push/

0x3: priority_queue

priority_queue 优先级队列是一个拥有权值概念的单向队列queue,在这个队列中,所有元素是按优先级排列的(也可以认为queue是个按进入队列的先后做为优先级的优先级队列——先进入队列的元素优先权要高于后进入队列的元素)

Example

// priority_queue::push/pop
#include <iostream>       // std::cout
#include <queue>          // std::priority_queue

int main ()
{
    std::priority_queue<int> mypq;

    mypq.push(30);
    mypq.push(100);
    mypq.push(25);
    mypq.push(40);

    std::cout << "Popping out elements...";
    while (!mypq.empty())
    {
        std::cout << ' ' << mypq.top();
        mypq.pop();
    }
    std::cout << '\n';

    return 0;
}

Relevant Link:

http://www.cplusplus.com/reference/queue/priority_queue/?kw=priority_queue
http://www.cplusplus.com/reference/queue/priority_queue/push/

在继续学习STL算法之前,我们对STL容器的特性和优缺点进行一个梳理总结

在理解了STL各个容器的底层实现数据结构的基础上,我们就可以理解针对这些容器的对应存储方式,之所以有的是单头操作,有的是双头操作,有的可以随意存取,有的任意位置插入,本质上都和它们的数据结构有关

 

3. C++ STL算法

undone

Relevant Link:

http://www.cnblogs.com/nanke/archive/2011/05/10/2042662.html

 

4. C++ STL边界限制

undone

 

Copyright (c) 2014 LittleHann All rights reserved

posted @ 2014-10-08 17:33  郑瀚Andrew  阅读(923)  评论(0编辑  收藏  举报