RSS
热门关键字:
当前位置 : 主页>编程开发>C/C++开发>列表

走近 STL

来源:我要研发网 作者: 时间:1970-01-01 点击:



本文示例源代码下载

字串4

  本文面向读者:学习过C 程序设计语言(也就是说学习过Template),但是还没有接触过STLSTL初学者。这实际上是我学习STL一篇笔记,老鸟就不用看了。 字串5

  什么是泛型程序设计 字串8

  我们可以简单理解为:使用模板程序设计就是泛型程序设计。就像我们我们可以简单理解面向对象程序设计就是使用虚函数程序设计一样。 字串6

  STL是什么 字串9

  作为一个C 程序设计者,STL是一种不可忽视技术。Sandard Template Library (STL): 字串1

  标准模板库,更准确说是 C 程序设计语言标准模板库。学习过MFC人知道,MFC是微软公司创建 C 类库。而与之类似是 STL 是模板库,只不过 STL 是 ANSI/ISO 标准一部分,而 MFC 只不过是微软一个产品而已。也就是说STL是所有C 编译器和所有操作系统平台都支持一种库,说它是一种库是因为,虽然STL是一种标准,也就是说对所有编译器来说,提供给C 程序设计者接口都是一样。也就是说同一段STL代码在不同编译器和操作系统平台上运行结果都是相同,但是底层实现可以是不同。 令人兴奋是,STL使用者并不需要了解它底层实现。 试想一下,如果我们有一把能打开所有锁钥匙,那将是多么令人疯狂啊。嘎嘎。这个歪梦我做了20多年鸟。 字串1

  STL是标准化组件,这样你就不用重新开发它们了。你可以仅仅使用这些现成组件。STL现在是C 一部分,因此不用额外安装什么。它被内建在你编译器之内。 字串8

  为什么我们需要学习STL

  • STL是 C ANSI/ISO 标准一部分,可以用于所有C 语言编译器和所有平台(Windows/Unix/Linux..)。STL同一版本在任意硬件配置下都是可用
  • STL 提供了大量可复用软件组织。例如,程序员再也不用自己设计排序,搜索算法了,这些都已经是STL一部分了。嘎嘎,有意思吧;
  • 使用STL 应用程序保证了得到实现在处理速度和内存利用方面都是高效,因为STL设计者们已经为我们考虑了;
  • 使用STL编写代码更容易修改和阅读,这是当然鸟。因为代码更短了,很多基础工作代码已经被组件化了;
  • 使用简单,虽然内部实现很复杂;
  •   虽然,STL优点甚多,但是STL语法实在令初学者人头疼,许多人望而却步。可是STL是每个C 程序设计者迟早都要啃一块骨头。因为越来越多C 代码是用STL编写,看不懂麻烦就大鸟。越来越多人在用STL,不懂就无法和别人一起合作了。事多磨嘛,早点学习早点解脱。 字串3


    字串7

      下面让我们来看几段代码吧:(你觉得头疼就不要看了) 字串8

    //stl_cpp_1.cpp
    #include
    double mean(double *array, size_t n)
    {
      double m=0;
      for(size_t i=0; i    m = array[i];
      }
      return m/n;
    }
    int main(void)
    {
      double a[] = {1, 2, 3, 4, 5};
      std::cout<  return 0;
    }
    懂吧,除了那个std有点让人不舒服以外?这是一段普通没有使用STLC 代码。再看下面一段://stl_cpp_2.cpp
    #include
    #include
    int main(void)
    {
      std::vector a;
      std::vector::const_iterator i;
      a.push_back(1);
      a.push_back(2);
      a.push_back(3);
      a.push_back(4);
      a.push_back(5);
      for(i=a.begin(); i!=a.end(); i){
        std::cout<<(*i)<  }
      return 0;
    }
      如果你真没有接触过STL话,你会问,呀,vector 是啥呀?我会告诉你,那是一排美女。嘎嘎。这可不是个比喻,表想歪鸟。这是一段纯种STL代码,看到尖括号了吧,知道那是模板了吧。看到a.push_back(5),a.begin(),a.end()你不感觉奇怪么?可是我们并没有定义这些函数啊。//stl_cpp_3.cpp

    字串1


    #include
    #include
    int main(void)
    {
      std::vector q;
      q.push_back(10);
      q.push_back(11);
      q.push_back(12);
      std::vector v;
      for(int i=0; i<5; i){
        v.push_back(i);
      }
      std::vector::iterator it = v.begin() 1;
      it = v.insert(it, 33);
      v.insert(it, q.begin(), q.end());
      it = v.begin() 3;
      v.insert(it, 3, -1);
      it = v.begin() 4;
      v.erase(it);
      it = v.begin() 1;
      v.erase(it, it 4);
      v.clear();
      return 0;
    }
      这一段你又看到了新东西了吧,iterator???不罗嗦了,等你看完这篇文章,回头再看就简单了。在正式介绍STL之前,我们需要花点时间来了解一下模板和命名空间。 字串2


    字串4

      关于模板其他细节,读者可以参阅《C Templates 中文版》(有点费脑子哦)。在这里,我只简单介绍一下模板类和函数模板概念。 字串1

      模板是C 中实现代码重用机制一种工具,可以实现类型参数化,把类型定义为参数。函数模板和类模板允许用户构造模板函数和模板类。 字串3

      

      图1 字串1

      下面我们来看一段函数模板例子: 字串2

    //stl_cpp_4.cpp
    #include
    #include
    //定义函数模板
    template //template 是关键字,T 表示一种待实例化类型
    //template 也是对
    T max(T a, T b)//函数模板,函数名为 max,此函数有2个T类型参数,返回类型为T
    {
     return (a>b)?a:b; 
    }
    //在此例实例化时候,T可以是多种类型,int,char,string…
    int main(void)
    {
     int x=2,y=6;
     double x1=9.123,y1=12.6543;
     cout<<"把T实例化为int:"< cout<<"把T实例化为double:"< //实例化函数模板,把T实例化为double
     getchar(); //这一行代码用来在dos下查看结果,也可以用cin.get();
    }
    下面再看看,类模板://stl_cpp_5.cpp
    #include
    //定义名为ex_class类模板 字串9
    template < typename T> class ex_class
    {
      T value;
    public:
      ex_class(T v) { value=v; }
      void set_value(T v) { value=v; }
      T get_value(void) {return value;}
    };
    //main()函数中测试ex_class类模板
    int main(void)
    {
      //测试int类型数据
      ex_class a(5),b(10);
      cout<<"a.value:"<  cout<<"b.value:"<  //测试char类型数据
      ex_class ch(''A'');
      cout<<"ch.value:"<  ch.set_value(''a'');
      cout<<"ch.value:"<  //测试double类型数据
      ex_class x(5.5);
      cout<<"x.value:"<  x.set_value(7.5);
      cout<<"x.value:"<}
    命名空间(名字空间)
    字串3


    字串2

      命名空间是C 一种机制,用来把单个标识符下大量有逻辑联系程序实体组合到一起。此标识符作为此组群名字。命名空间用关键字namespace 来定义://stl_cpp_6.cpp
    #include
    using namespace std;
    namespace printA
    {
      print() {cout<<"using namespace printA….."<}
    namespace printB
    {
      print() {cout<<"using namespace printB….."<}
    int main(void)
    {
      printA::print();  //测试命名空间printA, ::是作用域解析运算符
      printB::print(); 
    }
    命名空间可以嵌套定义:
     namespace A
    {
      functiong1(){};
      namespace B
      { }
    }
      一个namespace是指一个具名范围(named scope)。namespace被用来将相关声明划归在一起,将不相关代码部分隔开。命名空间只是命名了一个特殊作用域,当程序很大,而且需要多人合作时候,命名空间就显得特别重要。比如2个程序员A,B 在同一个程序中定义了函数 pop(),如果没有使用命名空间,则会出错,而且这种错误难以检测出来。为了安全起见,他们可以定义不同命名空间 A和B,在用时候可以使用A::pop()和B::pop()来区分。 字串6

      在STL中,标准库全部成员在预先定义命名空间std中。如果要用类模板vector ,有两种方法:一是在程序前面添加预处理指令:  #include
      using namespace std;
    第二种方法是:  #include
      using std::vector;
    动态绑定和静态绑定 字串5

      所谓绑定是指,对于参与多态行为类型,他们具有多态行为接口是在公共基类设计中就预先确定。而非绑定则对于参与多态行为类型,他们接口没有预先定义。 字串4

      在C 中通过继承实现多态是动态绑定,通过模板实现多态是静态绑定。动态绑定接口是在运行期间(动态)完成,静态绑定接口是在编译期间(静态)完成了,有了以上知识我们可以来学习STL 了。 字串2

      STL 组成 字串9

      STL有三大核心部分:容器(Container)、算法(Algorithms)、迭代器(Iterator),容器适配器(container adaptor),函数对象(functor),除此之外还有STL其他标准组件。

  • 容器:装东西东西,装水杯子,装咸水大海,装人教室……STL里容器是可容纳一些数据模板类;
  • 算法:就是往杯子里倒水,往大海里排污,从教室里撵人……STL里算法,就是处理容器里面数据方法,操作;
  • 迭代器:往杯子里倒水水壶,排污管道,撵人那个物业管理人员……STL里迭代器:遍历容器中数据对象;
  •   对存储于容器中数据进行处理时,迭代器能从一个成员移向另一个成员。他能按预先定义顺序在某些容器中成员间移动。对普通一维数组、向量、双端队列和列表来说,迭代器是一种指针。 字串8


    字串8

      知道了吧?嘎嘎,当然了,你猜到了,那是我在瞎扯蛋。

    字串1

      下面让我们来看看专家是怎么说

    字串1

  • 容器(container):容器是数据在内存中组织方法,例如,数组、堆栈、队列、链表或二叉树(不过这些都不是STL标准容器)。STL中容器是一种存储T(Template)类型值有限集合数据结构,容器内部实现一般是类。这些值可以是对象本身,如果数据类型T代表是Class话。
  • 算法(algorithm):算法是应用在容器上以各种方法处理其内容行为或功能。例如,有对容器内容排序、复制、检索和合并算法。在STL中,算法是由模板函数表现。这些函数不是容器类成员函数。相反,它们是独立函数。令人吃惊特点之一就是其算法如此通用。不仅可以将其用于STL容器,而且可以用于普通C++数组或任何其他应用程序指定容器。
  • 迭代器(iterator):一旦选定一种容器类型和数据行为(算法),那么剩下唯一要他做就是用迭代器使其相互作用。可以把达代器看作一个指向容器中元素普通指针。可以如递增一个指针那样递增迭代器,使其依次指向容器中每一个后继元素。迭代器是STL一个关键部分,因为它将算法和容器连在一起。
  •   下面我将依次介绍STL这三个主要组件。

    字串1

      容器

    字串8

      STL中容器有队列容器和关联容器,容器适配器(congtainer adapters:stack,queue,priority queue),位集(bit_set),串包(string_package)等等。

    字串1

      在本文中,我将介绍list,vector,deque等队列容器,和set和multisets,map和multimaps等关联容器,一共7种基本容器类。 字串6

      队列容器(顺序容器):队列容器按照线性排列来存储T类型值集合,队列每个成员都有自己特有位置。顺序容器有向量类型、双端队列类型、列表类型三种。

    字串2

      基本容器——顺序容器

    字串6

      向量(vector容器类):#include ,vector是一种动态数组,是基本数组类模板。其内部定义了很多基本操作。既然这是一个类,那么它就会有自己构造函数。vector 类中定义了4中种构造函数: 字串5

  • 默认构造函数,构造一个初始长度为0空向量, 字串8

      如:vector v1;

  • 带有单个整形参数构造函数,此参数描述了向量初始大小。这个构造函数还有一个可选参数,这是一个类型为T实例,描述了各个向量种各成员初始值;
    字串6

      如:vector v2(init_size,0); 如果预先定义了:int init_size;他成员值都被初始化为0;

  • 复制构造函数,构造一个新向量,作为已存在向量完全复制,
    字串2


    字串6

      如:vector v3(v2);

  • 带两个常量参数构造函数,产生初始值为一个区间向量。区间由一个半开区间[first,last](MS word显示可能会有问题,first前是一个左方括号,last后面是一个右圆括号)来指定。 字串1

      如:vector v4(first,last)

  •   下面一个例子用是第四种构造方法,其它方法读者可以自己试试。 字串9

    //stl_cpp_7.cpp
    //程序:初始化演示
    #include  
    #include
    #include
    using namespace std;
    int ar[10] = { 12, 45, 234, 64, 12, 35, 63, 23, 12, 55 };
    char* str = "Hello World";
    int main(void)
    {
     vector vec1(ar, ar 10);     //first=ar,last=ar 10,不包括ar 10
     vector vec2(str, str strlen(str)); //first=str,last= str strlen(str),不包括最后一个
     cout<<"vec1:"<//打印vec1和vec2,const_iterator是迭代器,后面会讲到
    //当然,也可以用for (int i=0; i//size()是vector一个成员函数
     for(vector::const_iterator p=vec1.begin();p!=vec1.end(); p)
       cout<<*p;
     cout<<''
    ''<<"vec2:"< for(vector::const_iterator p1=vec2.begin();p1!=vec2.end(); p1)
       cout<<*p1;
     getchar();
     return 0;
    } 
      为了帮助理解向量概念,这里写了一个小例子,其中用到了vector成员函数:begin(),end(),push_back(),assign(),front(),back(),erase(),empty(),at(),size()。//stl_cpp_8.cpp 字串1
    #include
    #include
    using namespace std;
    typedef vector INTVECTOR;//自定义类型INTVECTOR
    //测试vector容器功能
    void main(void)
    {
      //vec1对象初始为空
      INTVECTOR vec1; 
      //vec2对象最初有10个值为6元素 
      INTVECTOR vec2(10,6); 
      //vec3对象最初有3个值为6元素,拷贝构造
      INTVECTOR vec3(vec2.begin(),vec2.begin() 3); 
      //声明一个名为i双向迭代器
      INTVECTOR::iterator i;
      //从前向后显示vec1中数据
      cout<<"vec1.begin()--vec1.end():"<  for (i =vec1.begin(); i !=vec1.end(); i)
        cout << *i << " ";
      cout << endl;
      //从前向后显示vec2中数据
      cout<<"vec2.begin()--vec2.end():"<  for (i =vec2.begin(); i !=vec2.end(); i) 字串2
        cout << *i << " ";
      cout << endl;
      //从前向后显示vec3中数据
      cout<<"vec3.begin()--vec3.end():"<  for (i =vec3.begin(); i !=vec3.end(); i)
        cout << *i << " ";
      cout << endl;
      //测试添加和插入成员函数,vector不支持从前插入
      vec1.push_back(2);//从后面添加一个成员
      vec1.push_back(4);
    vec1.insert(vec1.begin() 1,5);//在vec1第一个位置上插入成员5
    //从vec1第一位置开始插入vec3所有成员
    vec1.insert(vec1.begin() 1,vec3.begin(),vec3.end());
    cout<<"after push() and insert() now the vec1 is:" <  for (i =vec1.begin(); i !=vec1.end(); i)
        cout << *i << " ";
      cout << endl;
      //测试赋值成员函数
      vec2.assign(8,1);  // 重新给vec2赋值,8个成员初始值都为1
      cout<<"vec2.assign(8,1):" <字串4

      for (i =vec2.begin(); i !=vec2.end(); i)
        cout << *i << " ";
      cout << endl;
      //测试引用类函数
      cout<<"vec1.front()="<  cout<<"vec1.back()="<最后一个成员
      cout<<"vec1.at(4)="<第五个成员
      cout<<"vec1[4]="<  //测试移出和删除
      vec1.pop_back();//将最后一个成员移出vec1
      vec1.erase(vec1.begin() 1,vec1.end()-2);//删除成员
      cout<<"vec1.pop_back() and vec1.erase():" <  for (i =vec1.begin(); i !=vec1.end(); i)
        cout << *i << " ";
      cout << endl;
      //显示序列状态信息
      cout<<"vec1.size(): "<  cout<<"vec1.empty(): "<}
      push_back()是将数据放入vector(向量)或deque(双端队列)标准函数。Insert()是一个与之类似函数,然而它在所有容器中都可以使用,但是用法更加复杂。end()实际上是取末尾加一,以便让循环正确运行--它返回指针指向最靠近数组界限数据。 字串1


    字串5

      在Java里面也有向量概念。Java中向量是对象集合。其中,各元素可以不必同类型,元素可以增加和删除,不能直接加入原始数据类型。

    字串6

      双端队列(qeque容器类):#include

    字串5

      deque(读音:deck,意即:double queue)容器类与vector类似,支持随机访问和快速插入删除,它在容器中某一位置上操作所花费是线性时间。与vector不同是,deque还支持从开始端插入数据:

    字串5

      push_front()。此外deque也不支持与vectorcapacity()、reserve()类似操作。//stl_cpp_9.cpp
    #include
    #include
    using namespace std;
    typedef deque INTDEQUE;//有些人很讨厌这种定义法,呵呵
    //从前向后显示deque队列全部元素
    void put_deque(INTDEQUE deque, char *name)
    {
      INTDEQUE::iterator pdeque;//仍然使用迭代器输出
      cout << "The contents of " << name << " : ";
      for(pdeque = deque.begin(); pdeque != deque.end(); pdeque )
        cout << *pdeque << " ";//注意有 "*"号哦,没有"*"号话会报错
      cout<}
    //测试deqtor容器功能
    void main(void)
    {
      //deq1对象初始为空
      INTDEQUE deq1; 
      //deq2对象最初有10个值为6元素 
      INTDEQUE deq2(10,6);  字串6
      //deq3对象最初有3个值为6元素 
      //声明一个名为i双向迭代器变量
      INTDEQUE::iterator i;
      //从前向后显示deq1中数据
      put_deque(deq1,"deq1");
      //从前向后显示deq2中数据
      put_deque(deq2,"deq2");
      //从deq1序列后面添加两个元素
      deq1.push_back(2);
      deq1.push_back(4);
      cout<<"deq1.push_back(2) and deq1.push_back(4):"<  put_deque(deq1,"deq1");
      //从deq1序列前面添加两个元素
      deq1.push_front(5);
      deq1.push_front(7);
      cout<<"deq1.push_front(5) and deq1.push_front(7):"<  put_deque(deq1,"deq1");
      //在deq1序列中间插入数据
      deq1.insert(deq1.begin() 1,3,9);
      cout<<"deq1.insert(deq1.begin() 1,3,9):"<  put_deque(deq1,"deq1");
      //测试引用类函数
      cout<<"deq1.at(4)="<  cout<<"deq1[4]="<字串3
      deq1.at(1)=10;
      deq1[2]=12;
      cout<<"deq1.at(1)=10 and deq1[2]=12 :"<  put_deque(deq1,"deq1");
      //从deq1序列前后各移去一个元素
      deq1.pop_front();
      deq1.pop_back();
      cout<<"deq1.pop_front() and deq1.pop_back():"<  put_deque(deq1,"deq1");
      //清除deq1中第2个元素
      deq1.erase(deq1.begin() 1);
      cout<<"deq1.erase(deq1.begin() 1):"<  put_deque(deq1,"deq1");
      //对deq2赋值并显示
      deq2.assign(8,1);
      cout<<"deq2.assign(8,1):"<  put_deque(deq2,"deq2");
    }
      上面我们演示了deque如何进行插入删除等操作,像erase(),assign()是大多数容器都有操作。关于deque其他操作请参阅附录。

    字串4


    字串4

      表(List容器类):#include 字串9

       List又叫链表,是一种双线性列表,只能顺序访问(从前向后或者从后向前),图2是list数据组织形式。与  前面两种容器类有一个明显区别就是:它不支持随机访问。要访问表中某个下标处项需要从表头或表尾处(接近该下标一端)开始循环。而且缺少下标预算符:operator[]。 字串4

      

      图2

    字串5

      同时,list仍然包涵了erase(),begin(),end(),insert(),push_back(),push_front()这些基本函数,下面我们来演示一下list其他函数功能。

    字串7

      merge():合并两个排序列表; 字串6

      splice():拼接两个列表;

    字串7

      sort():列表排序;//stl_cpp_10.cpp
    #include
    #include
    #include
    using namespace std;
    void PrintIt(list n)
    {
      for(list::iterator iter=n.begin(); iter!=n.end(); iter)
       cout<<*iter<<" ";//用迭代器进行输出循环
      }
    int main(void)
    {
      list listn1,listn2;
      //给listn1,listn2初始化
      listn1.push_back(123);
      listn1.push_back(0);
      listn1.push_back(34);
      listn1.push_back(1123);
      //now listn1:123,0,34,1123
      listn2.push_back(100);
      listn2.push_back(12);
      //now listn2:12,100
      listn1.sort();
      listn2.sort();
      //给listn1和listn2排序
      //now listn1:0,34,123,1123     listn2:12,100
      PrintIt(listn1);
      cout<  PrintIt(listn2);
      listn1.merge(listn2);
      //合并两个排序列表后,listn1:0,12,34,100,123,1123
      cout<  PrintIt(listn1);
      cin.get();
    }
      上面并没有演示splice()函数用法,这是一个拗口函数。用起来有点麻烦。图3所示是splice函数功能。将一个列表插入到另一个列表当中。list容器类定义了splice()函数3个版本:splice(position,list_value);

    字串9


    splice(position,list_value,ptr);
    splice(position,list_value,first,last);
      list_value是一个已存在列表,它将被插入到源列表中,position是一个迭代参数,他当前指向是要进行拼接列表中特定位置。
    字串5


    字串9

      

      图3

    字串7

    listn1:123,0,34,1123  listn2:12,100  执行listn1.splice(find(listn1.begin(),listn1.end(),0),listn2);之后,listn1将变为:123,12,100,34,1123。即把listn2插入到listn10这个元素之前。其中,find()函数找到0这个元素在listn1中位置。值得注意是,在执行splice之后,list_value将不复存在了。这个例子中是listn2将不再存在。

    字串4

      第二个版本当中ptr是一个迭代器参数,执行结果是把ptr所指向值直接插入到position当前指向位置之前.这将只向源列表中插入一个元素。 字串5

      第三个版本first和last也是迭代器参数,并不等于list_value.begin(),list_value.end()。First指是要插入第一个元素,last指是要插入最后一个元素。

    字串5

      如果listn1:123,0,34,1123 listn2:12,43,87,100 执行完以下函数之后listn1.splice(find(listn1.begin(),listn1.end(),0), listn2.begin(),--listn2.end());
    listn1:123,43,87,0,34,1123 listn2:12,100
      以上,我们学习了vector,deque,list三种基本顺序容器,其他顺序容器还有:slist,bit_vector等等。 字串3

      另一种容器——关联容器(有点费解哦,出去让脑子清醒一下再回来看) 字串7

      与前面讲到顺序容器相比,关联容器更注重快速和高效地检索数据能力。这些容器是根据键值(key)来检索数据,键可以是值也可以是容器中某一成员。这一类中成员在初始化后都是按一定顺序排字串4

      集和多集(set 和multiset 容器类):#include

    字串1

      一个集合(set)是一个容器,它其中所包含元素值是唯一。这在收集一个数据具体值时候是有用。集合中元素按一定顺序排列,并被作为集合中实例。如果你需要一个键/值对(pair)来存储数据,map(也是一个关联容器,后面将马上要讲到)是一个更选择。一个集合通过一个链表来组织,在插入操作和删除操作上比向量(vector)快,但查找或添加末尾元素时会有些慢。

    字串2


    字串4

      在集中,所有成员都是排列。如果先后往一个集中插入:12,2,3,123,5,65

    字串2

      则输出该集时为:2,3,5,12,65,123 字串9

      集和多集区别是:set支持唯一键值,set中值都是特定,而且只出现一次;而multiset中可以出现副本键,同一值可以出现多次。 字串2

      Set和multiset模板参数:template  第一个参数key是所存储类型,第二个参数是为排序值而定义比较函数类型,第三个参数是被实现存储分配符类型。在有些编译器具体实现中,第三个参数可以省略。第二个参数使用了合适形式迭代器为键定义了特定关系操作符,并用来在容器中遍历值时建立顺序。集迭代器是双向,同时也是常量,所以迭代器在使用时候不能修改元素值。 字串6

      Set定义了三个构造函数: 字串5

      默认构造函数:explicit set(const Compare&=compare());
    如:set > set1;
      less是一个标准类,用于形成降序排列函数对象。升序排列是用greater。通过指定某一预先定义区间来初始化set对象构造函数:template set(InputIterator, InputIterator, const Compare&=compare());
    如:set >set2(vector1.begin(),vector1.end());

      复制构造函数:

    字串8

    set(const set);
    如:set >set3(set2);

      下面我们来看一个简单集和多集插入例程:

    字串6

    //stl_cpp_11.cpp
    #include
    #include
    using namespace std;
    int main(void)
    {
      set set1;
      for(int i=0; i<10; i)
       set1.insert(i);
      for(set::iterator p=set1.begin();p!=set1.end(); p)
       cout<<*p<<"";
       if(set1.insert(3).second)//把3插入到set1中
    //插入成功则set1.insert(3).second返回1,否则返回0
    //此例中,集中已经有3这个元素了,所以插入将失败
        cout<<"set insert success";
       else
        cout<<"set insert failed";
      int a[] = {4, 1, 1, 1, 1, 1, 0, 5, 1, 0};
      multiset A;
    A.insert(set1.begin(),set1.end());
      A.insert(a,a 10);
      cout<  for(multiset::iterator p=A.begin();p!=A.end(); p)
      cout<<*p<<" ";
      cin.get();
      return 0;
    }
      在集之间可以进行并集(set_union())、交集(set_intersection())、差集(set_diffrence())d等操作,功能强大。 字串9


    字串3

      映射和多重映射(map 和multimap):#include 字串5

      映射和多重映射基于某一类型Key键集存在,提供对T类型数据进行快速和高效检索。对map而言,键只是指存储在容器中某一成员。Map不支持副本键,multimap支持副本键。Map和multimap对象包涵了键和各个键有关值,键和值数据类型是不相同,这与set不同。set中key和value是Key类型,而map中key和value是一个pair结构中两个分量。Map支持下表运算符operator[],用访问普通数组方式访问map,不过下标为map键。在multimap中一个键可以对应多个不同值。 字串7

      下面例程说明了map中键与值关系。//stl_cpp_12.cpp
    #include
    #include
    using namespace std;
    int main(void)
    {
      map > map1;
      map >::iterator mapIter;
      //char 是键类型,int是值类型
      //下面是初始化,与数组类似
    //也可以用map1.insert(map >::value_type(''c'',3));
    map1[''c'']=3;
      map1[''d'']=4;
      map1[''a'']=1;
      map1[''b'']=2;
      for(mapIter=map1.begin();mapIter!=map1.end(); mapIter)
       cout<<" "<<(*mapIter).first<<": "<<(*mapIter).second;
      //first对应定义中char键,second对应定义中int值 
      //检索对应于d键值是这样做字串9
      map >::const_iterator ptr;
      ptr=map1.find(''d'');
      cout<<''
    ''<<" "<<(*ptr).first<<" 键对应于值:"<<(*ptr).second;
      cin.get();
       return 0;
    }
      从以上例程中,我们可以看到map对象行为和一般数组行为类似。Map允许两个或多个值使用比较操作符。下面我们再看看multimap://stl_cpp_13.cpp
    #include
    #include
    #include
    using namespace std;
    int main(void)
    {
      multimap >mulmap;
      multimap >::iterator p;
      //初始化多重映射mulmap:
      typedef multimap >::value_type vt;
      typedef string s;
      mulmap.insert(vt(s("Tom "),s("is a student")));
      mulmap.insert(vt(s("Tom "),s("is a boy")));
      mulmap.insert(vt(s("Tom "),s("is a bad boy of blue!")));
      mulmap.insert(vt(s("Jerry "),s("is a student")));

    字串3


      mulmap.insert(vt(s("Jerry "),s("is a beatutiful girl")));
      mulmap.insert(vt(s("DJ "),s("is a student")));
      //输出初始化以后多重映射mulmap:
      for(p=mulmap.begin();p!=mulmap.end(); p)
        cout<<(*p).first<<(*p).second<  //检索并输出Jerry键所对应所有
      cout<<"find Jerry :"<  p=mulmap.find(s("Jerry "));
      while((*p).first=="Jerry ")
      {
        cout<<(*p).first<<(*p).second<     p;
      }  
      cin.get();
      return 0;
    } 
      在map中是不允许一个键对应多个值,在multimap中,不支持operator[],也就是说不支持map中允许下标操作。
    字串1


    字串1

      算法(algorithm):#inlcude

    字串2

      STL中算法大部分都不作为某些特定容器类成员函数,他们是泛型,每个算法都有处理大量不同容器类中数据使用。值得注意是,STL中算法大多有多种版本,用户可以依照具体情况选择合适版本。中在STL泛型算法中有4类基本算法:

  • 变序型队列算法,可以改变容器内数据;
  • 非变序型队列算法,处理容器内数据而不改变他们;
  • 排序值算法,包涵对容器中值进行排序和合并算法,还有二叉搜索算法 $$通用数值算法;
  •   注:STL算法并不只是针对STL容器,对一般容器也是适用字串7

      变序型队列算法(mutating algorithms):

    字串8

      又叫可修改序列算法。这类算法有复制(copy)算法、交换(swap)算法、替代(replace)算法、删除(remove)算法,移动(transfer)算法、翻转(reverse)算法等等。这些算法可以改变容器中数据(数据值和值在容器中位置)。下面介绍2个比较常用算法reverse()和copy()。

    字串7

    //stl_cpp_14.cpp
    #include
    #include
    #include //下面用到了输出迭代器ostream_iterator
    using namespace std;
    int main(void)
    {
      int arr[6]={1,12,3,2,1215,90};
       int arr1[7];
      int arr2[6]={2,5,6,9,0,-56};
      copy(arr,(arr 6),arr1);//将数组aar复制到arr1
      cout<<"arr[6] copy to arr1[7],now arr1: "<  for(int i=0;i<7;i )
        cout<<" "<  reverse(arr,arr 6);//将排arr翻转
      cout<<''
    ''<<"arr reversed ,now arr:"<  copy(arr,arr 6,ostream_iterator(cout, " "));//复制到输出迭代器
    swap_ranges(arr,arr 6,arr2);//交换arr和arr2序列
      cout<<''
    ''<<"arr swaped to arr2,now arr:"<  copy(arr,arr 6,ostream_iterator(cout, " "));
      cout<<''
    ''<<"arr2:"<  copy(arr2,arr2 6,ostream_iterator(cout, " "));
      cin.get(); 字串7
      return 0;
    }
    revese()功能是将一个容器内数据顺序翻转过来,它原型是: template
      void reverse(Bidirectional first, Bidirectional last);
    将first和last之间元素翻转过来,上例中你也可以只将arr中一部分进行翻转:  reverse(arr 3,arr 6);这也是有效。First和last需要指定一个操作区间。Copy()是要将一个容器内数据复制到另一个容器内,它原型是:  Template
     OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result);
      它把[first,last-1]内队列成员复制到区间[result,result (last-first)-1]中。泛型交换算法:Swap()操作是单值交换,它原型是:template

    字串4


    void swap(T& a,T& b);
    swap_ranges()操作是两个相等大小区间中值,它原型是: template
     ForwardIterator2 swap_ranges(ForwardIterator1 first1,ForwardIterator1 last1,
    ForwardIterator1 first2);
      交换区间[first1,last1-1]和[first2, first2 (last1-first1)-1]之间值,并假设这两个区间是不重叠
    字串5


    字串2

      非变序型队列算法(Non-mutating algorithm):

    字串6

      又叫不可修改序列算法。这一类算法操作不影响其操作容器内容,包括搜索队列成员算法,等价性检查算法,计算队列成员个数算法。我将用下面例子介绍其中find(),search(),count()://stl_cpp_15.cpp
    #include
    #include
    #include
    using namespace std;
    int main(void)
    {
      int a[10]={12,31,5,2,23,121,0,89,34,66};
      vector v1(a,a 10);
      vector::iterator result1,result2;//result1和result2是随机访问迭代器
      result1=find(v1.begin(),v1.end(),2);
      //在v1中找到2,result1指向v1中2
      result2=find(v1.begin(),v1.end(),8);
      //在v1中没有找到8,result2指向是v1.end() 字串6
      cout<  cout<  int b[9]={5,2,23,54,5,5,5,2,2};
      vector v2(a 2,a 8);
      vector v3(b,b 4);
      result1=search(v1.begin(),v1.end(),v2.begin(),v2.end());
      cout<<*result1<  //在v1中找到了序列v2,result1指向v2在v1中开始位置
       result1=search(v1.begin(),v1.end(),v3.begin(),v3.end());
       cout<<*(result1-1)<  //在v1中没有找到序列v3,result指向v1.end(),屏幕打印出v1最后一个元素66  
       vector v4(b,b 9);
       int i=count(v4.begin(),v4.end(),5);
       int j=count(v4.begin(),v4.end(),2);
       cout<<"there are "<   cout<<"there are "<   //计算v4中有多少个成员等于 5,2
      cin.get();
      return 0;    
    }
    find()原型是:template

    字串3

    InputIterator find(InputIterator first, InputIterator last,
              const EqualityComparable& value);
      其功能是在序列[first,last-1]中查找value值,如果找到,就返回一个指向value在序列中第一次出现迭代,如果没有找到,就返回一个指向last迭代(last并不属于序列)。 search()原型是:template
    ForwardIterator1 search(ForwardIterator1 first1, ForwardIterator1 last1,
                ForwardIterator2 first2, ForwardIterator2 last2);
      其功能是在源序列[first1,last1-1]查找目标序列[first2,last2-1]如果查找成功,就返回一个指向源序列中目标序列出现首位置迭代。查找失败则返回一个指向last迭代。 Count()原型是:template 字串4
    iterator_traits::difference_type count(InputIterator first,
    InputIterator last, const EqualityComparable& value);
      其功能是在序列[first,last-1]中查找出等于value成员,返回等于value得成员个数。

    字串5


    字串6

      排序算法(sort algorithm):

    字串7

      这一类算法很多,功能强大同时也相对复杂一些。这些算法依赖是关系运算。在这里我只介绍其中比较简单几种排序算法:sort(),merge(),includes()//stl_cpp_16.cpp
     #include
    #include
    using namespace std;
    int main(void)
    {
      int a[10]={12,0,5,3,6,8,9,34,32,18};
      int b[5]={5,3,6,8,9};
      int d[15];
      sort(a,a 10);
      for(int i=0;i<10;i )
       cout<<" "<  sort(b,b 5);
      if(includes(a,a 10,b,b 5))
        cout<<''
    ''<<"sorted b members are included in a."<  else
        cout<<"sorted a dosn`t contain sorted b!";
       merge(a,a 10,b,b 5,d);
      for(int j=0;j<15;j )
        cout<<" "<  cin.get();
      return 0;
    }
    sort()原型是:template
    void sort(RandomAccessIterator first, RandomAccessIterator last);
      功能是对[first,last-1]区间内元素进行排序操作。与之类似操作还有:partial_sort(), stable_sort(),partial_sort_copy()等等。 merge()原型是:template

    字串7


    OutputIterator merge(InputIterator1 first1, InputIterator1 last1,
            InputIterator2 first2, InputIterator2 last2,OutputIterator result);
      将有序区间[first1,last1-1]和[first2,last2-1]合并到[result, result (last1 - first1) (last2 - first2)-1]区间内。

    字串9

      Includes()原型是:template
    bool includes(InputIterator1 first1, InputIterator1 last1,
             InputIterator2 first2, InputIterator2 last2);
      其功能是检查有序区间[first2,last2-1]内元素是否都在[first1,last1-1]区间内,返回一个bool值。 字串9

      通用数值算法(generalized numeric algorithms)

    字串2

      这一类算法还不多,涉及到专业领域中有用算术操作,独立包涵于头文件中(HP版本STL中是)。这里不作介绍。 字串8


    字串5

      STL中算法大都有多种版本,常见版本有以下4中:

  • 默认版本,假设给出了特定操作符;
  • 一般版本,使用了成员提供操作符;
  • 复制版本,对原队列副本进行操作,常带有 _copy 后缀;
  • 谓词版本,只应用于满足给定谓词队列成员,常带有 _if 后缀;
  •   以上我们学习了STL容器和算法概念,以及一些简单STL容器和算法。在使用算法处理容器内数据时,需要从一个数据成员移向另一个数据成员,迭代器恰实现了这一功能。下面我们来学习STL迭代器 。

    字串6

      迭代器(itertor):#include 字串1

      迭代器实际上是一种泛化指针,如果一个迭代器指向了容器中某一成员,那么迭代器将可以通过自增自减来遍历容器中所有成员。迭代器是联系容器和算法媒介,是算法操作容器接口。在运用算法操作容器时候,我们常常在不知不觉中已经使用了迭代器。

    字串9

      STL中定义了6种迭代器: 字串4

  • 输入迭代器,在容器连续区间内向前移动,可以读取容器内任意值;
  • 输出迭代器,把值写进它所指向队列成员中;
  • 前向迭代器,读取队列中值,并可以向前移动到下一位置( p,p );
  • 双向迭代器,读取队列中值,并可以向前向后遍历容器;
  • 随机访问迭代器, vector::iterator,list::iterator等都是这种迭代器 ;
  • 流迭代器,可以直接输出、输入流中值;
  •   实际上,在前面例子中,我们不停在用迭代器。下面我们用几个例子来帮助理解这些迭代器用法。 字串4

      下面例子用到了输入输出迭代器: 字串5

    // stl_cpp_17.cpp
    #include
    #include
    #include
    #include
    #include
    using namespace std;
    int main(void)
    {
      vector v1;
      ifstream file("Text1.txt");
      if(file.fail())
      {
       cout<<"open file Text1.txt failed"<   return 1;
      }  
     copy(istream_iterator(file),istream_iterator(),inserter(v1,
    v1.begin()));
      copy(v1.begin(),v1.end(),ostream_iterator(cout," "));
      cout<  cin.get();
      return 0;
    }
      这里用到了输入迭代器istream_iterator,输出迭代器ostream_iterator。程序完成了将一个文件输出到屏幕功能,先将文件读入,然后通过输入迭代器把文件内容复制到类型为字符串向量容器内,最后由输出迭代器输出。Inserter是一个输入迭代器一个函数(迭代器适配器),它使用方法是:inserter (container ,pos);

      congtainer是将要用来存入数据容器,pos是容器存入数据开始位置。上例中,是把文件内容存入(copy())到向量v1中。

    字串1


    字串9

      现在我们已经对STL三大基本组件有了一个大概了解,下面让我们一起来看看STL其他标准组件。 字串1

      函数对象(functor或者funtion objects):#include

    字串4

      函数对象又称之为仿函数。函数对象将函数封装在一个对象中,使得它可作为参数传递给合适STL算法,从而使算法功能得以扩展。可以把它当作函数来使用。用户也可以定义自己函数对象。下面让我们来定义一个自己函数对象。

    字串8

    // stl_cpp_18.cpp
    #include
    using namespace std;
    struct int_max{
      int operator()(int x,int y){return x>y?x:y; }
      };//operator() 重载了"()", (int x,int y)是参数列表
    int main(void)
    {
      cout<  cin.get();
      return 0;
    }
      这里int_max()就是一个函数对象,struct关键字也可以用class来代替,只不过struct默认情况下是公有访问权限,而class定义是默认私有访问权限。下面我们来定义一个STL风格函数对象:// stl_cpp_19.cpp
    #include
    #include
    using namespace std;
    struct adder : public unary_function
      {
       adder() : sum(0) {}
       double sum;
       void operator()(double x) { sum = x; }
      };
    int main(void)
    { 
      double a[5]={0.5644,1.1,6.6,8.8,9.9};
      vector V(a,a 5);
      adder result = for_each(V.begin(), V.end(), adder());

    字串2


      cout << "The sum is " << result.sum << endl;
      cin.get();
      return 0;
    }
      在这里,我们定义了一个函数对象adder(),这也是一个类,它基类是unary_function函数对象。unary_function是一个空基类,不包涵任何操作或变量。只是一种格式说明,它有两个参数,第一个参数是函数对象使用数据类型,第二个参数是它返回类型。基于它所定义函数对象是一元函数对象。(注:用关键字struct或者class定义类型实际上都是"类")
    字串5

      STL内定义了各种函数对象,否定器、约束器、一元谓词、二元谓词都是常用函数对象。函数对象对于编程来说很重要,因为他如同对象类型抽象一样作用于操作。

    字串3


    字串4

      适配器(adapter):

    字串3

      适配器是用来修改其他组件接口STL组件,是带有一个参数类模板(这个参数是操作数据类型)。STL定义了3种形式适配器:容器适配器,迭代器适配器,函数适配器。

  • 容器适配器:包括栈(stack)、队列(queue)、优先(priority_queue)。使用容器适配器,stack就可以被实现为基本容器类型(vector,dequeue,list)适配。可以把stack看作是某种特殊vctor,deque或者list容器,只是其操作仍然受到stack本身属性限制。queue和priority_queue与之类似。容器适配器接口更为简单,只是受限比一般容器要多;
  • 迭代器适配器:修改为某些基本容器定义迭代器接口一种STL组件。反向迭代器和插入迭代器都属于迭代器适配器,迭代器适配器扩展了迭代器功能;
  • 函数适配器:通过转换或者修改其他函数对象使其功能得到扩展。这一类适配器有否定器(相当于"非"操作)、帮定器、函数指针适配器。
  •   结束语 字串7

      如果你理解了算法、迭代器、容器,那么你几乎就了解了 STL。关于STL其他方面,新手都是不常用,可以暂时以理解STL组成编程思想为主。这篇文章里用到了19个cpp代码,每个代码都在Windows 2000 Dev-C 4.9.9.0和windows 2000+VC环境下通过编译运行。读者可以通过copy/paste到任何一款C 编译器中运行。无论你想不想学STL,先运行一下STL代码吧。编程快乐,学习,天天向上。 字串9

      我是新手,欢迎高手批评,欢迎STL学习者交流: Email:taohanjunjiang@yahoo.com.cn QQ:370679790

    字串4

      【附录】 字串8

      附录一:Dev-C 和VC 字串1

      在Dev-C 4.9.9.0 windows 2000下,不允许出现,void main(){} 而必须为 int main() {return 0;} ,或者 int main(){},奇怪是VC对 int main(){} 写法会提出警告,而必须为 int main() {return 0;}。建议使用标准格式 int main(void) {……return 0;} 字串8

      Dev-C 而且不支持头文件方式,#include 是不对,而必须为#include using namespace std; VC两种格式都支持。建议使用#include using namespace std;

    字串9

      如果用纯C/C 编程,建议使用比较简单C 编译器,甚至是命令行下编译方式。功能强大IDE甚至会误导编程者。还要花费不少时间来学习使用IDE开发环境。Dev比VC要简单一些,只是不适合做大型工程,而且编译时间比VC稍慢一点,纯C 编程建议使用。他有插入时间和头文件注释模板功能,作者可以方便地插入编程、程序更改时间,方便地填写程序说明信息。

    字串9


    字串8

      在Dev下可以导入VC工程,而且保持原来VC工程文件,但是Dev对VC支持还不够,在编译时候会遇到错误。

    字串1

      在编写dos程序时候,建议在程序末尾return之前加上:getchar();cin.get();之类代码,以方便看运行结果。

    字串5

      附录二:中小型程序段编辑工具 字串4

      在编辑程序过程中并不一定非要在特定IDE环境中,在有些专业化文本编辑工具中编辑代码会更有利于代码修改和编辑。这里介绍几种更能比较强大文本编辑器。 字串2

  • UltraEdit-32;
  • EditPlus;
  • SourceInsight;
  • Vim;
  •   这后面是写给Jerry,你个垃圾别说不看哦。据

    最新评论共有 0 位网友发表了评论
    发表评论
    评论内容:不能超过250字,需审核,请自觉遵守互联网相关政策法规。
    用户名: 密码:
    匿名?
    注册
    热点关注
    相关文章
    相关文章
    媒体推荐链接