STL

STL简介

1.作为一个C++程序设计者,STL是一种不可忽视的技术。

Standard Template Library (STL):标准模板库,更准确的说是 C++ 程序设计语言标准模板库。STL是所有C++编译器和所有操作系统平台都支持的一种库,说它是一种库是因为,虽然STL是一种标准,也就是说对所有的编译器来说,提供给C++程序设计者的接口都是一样的。也就是说同一段STL代码在不同编译器和操作系统平台上运行的结果都是相同的,但是底层实现可以是不同的。令人兴奋的是,STL的使用者并不需要了解它的底层实现。 试想一下,如果我们有一把能打开所有锁的钥匙,那将是多么令人疯狂啊。

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

2.      为什么我们需要学习STL

STL是 C++的ANSI/ISO 标准的一部分,可以用于所有C++语言编译器和所有平台(Windows/Unix/Linux..)。STL的同一版本在任意硬件配置下都是可用的;

STL 提供了大量的可复用软件组织。例如,程序员再也不用自己设计排序,搜索算法了,这些都已经是STL的一部分了。嘎嘎,有意思吧。

使用STL 的应用程序保证了得到的实现在处理速度和内存利用方面都是高效的,因为STL设计者们已经为我们考虑好了。

使用STL编写的代码更容易修改和阅读,这是当然的啦。因为代码更短了,很多基础工作代码已经被组件化了;

使用简单,虽然内部实现很复杂。

虽然,STL的优点甚多,但是STL的语法实在令初学者人头疼,许多人望而却步。可是STL是每个C++程序设计者迟早都要啃的一块骨头。

3.      初识STL

下面让我们来看几段代码吧:

1 #include <iostream>
2 int main(void)
3 {
4     double a[] = {1, 2, 3, 4, 5};
5     std::cout<<mean(a, 5)<<std::endl;    // will print 3
6     return 0;
7 }

好懂吧,除了那个std有点让人不舒服以外,这是一段普通的没有使用STL的C++代码。

再看下面一段:

 1 #include <vector>
 2 #include <iostream>
 3 int main()
 4 {
 5     std::vector<double> a;
 6     a.push_back(1);
 7     a.push_back(2);
 8     a.push_back(3);
 9     a.push_back(4);
10     a.push_back(5);
11     for(int i = 0; i < a.size(); ++i)
12 {
13         std::cout<<a[i]<<std::endl;
14     }
15     return 0;
16 }

如果你真的没有接触过STL的话,你会问,呀,vector 是啥呀?这是一段纯种的STL代码,看到尖括号了吧,知道那是模板了吧。看到a.push_back(5)、a.size()你不感觉奇怪么?可是我们并没有定义这些函数啊。

 1 #include <vector>
 2 #include <iostream>
 3 int main()
 4 {
 5     std::vector< int > q;
 6     q.push_back(10);
 7     q.push_back(11);
 8     q.push_back(12);
 9     std::vector< int > v;
10     for(int i=0; i<5; ++i){
11         v.push_back(i);
12     }
13     std::vector<int>::iterator it = v.begin() + 1;
14     it = v.insert(it, 33);
15     v.insert(it, q.begin(), q.end());
16     it = v.begin() + 3;
17     v.insert(it, 3, -1);
18     it = v.begin() + 4;
19     v.erase(it);
20     it = v.begin() + 1;
21     v.erase(it, it + 4);
22     v.clear();
23     return 0;
24 }

这一段你又看到了新东西了吧:iterator、insert、erase、clear。不罗嗦了,等你看完这篇文章,回头再看就简单了。

关于模板的其他细节,读者可以参阅《C++ Templates 中文版》在这里,简单的介绍一下模板类和函数模板的概念。

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

下面我们来看一段函数模板的例子:

 

 1 #include<iostream>
 2 #include<string>
 3 using namespace std;
 4 //定义函数模板
 5 template<class T>   //template 是关键字,T 表示一种待实例化的类型
 6                     //template<typename T> 也是对的
 7 T MAX(T a, T b)//函数模板,函数名为 max,此函数有2个T类型的参数,返回类型为T
 8 {
 9 return (a>b)?a:b;
10 }
11 //在此例实例化的时候,T可以是多种类型的,int,char,string…
12 int main()
13 {
14 int x=2,y=6;
15    double x1=9.123,y1=12.6543;
16    cout<<"把T实例化为int:"<<MAX(x,y)<<endl;//实例化函数模板,把T实例化为int
17    cout<<"把T实例化为double:"<<MAX(x1,y1)<<endl; //把T实例化为double
18 }
19 下面再看看,类模板:
20 #include<iostream>
21 using namespace std;
22 //定义名为ex_class的类模板
23 template < typename T> class ex_class
24 {
25     T value;
26 public:
27     ex_class(T v) { value=v; }
28     void set_value(T v) { value=v; }
29     T get_value(void) {return value;}
30 };
31 //main()函数中测试ex_class类模板
32 int main()
33 {
34     //测试int类型数据
35     ex_class <int> a(5),b(10);
36     cout<<"a.value:"<<a.get_value()<<endl;
37     cout<<"b.value:"<<b.get_value()<<endl;
38     //测试char类型数据
39     ex_class <char> ch('A');
40     cout<<"ch.value:"<<ch.get_value()<<endl;
41     ch.set_value('a');
42     cout<<"ch.value:"<<ch.get_value()<<endl;
43     //测试double类型数据
44     ex_class <double> x(5.5);
45     cout<<"x.value:"<<x.get_value()<<endl;
46     x.set_value(7.5);
47     cout<<"x.value:"<<x.get_value()<<endl;
48 }

< STL>的组成

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

容器:装东西的东西,装水的杯子,装咸水的大海,装人的教室……STL里的容器是可容纳一些数据的模板类。

算法:就是往杯子里倒水,往大海里排污,从教室里撵人……STL里的算法,就是处理容器里面数据的方法、操作。

迭代器:往杯子里倒水的水壶,排污的管道,撵人的那个物业管理人员……STL里的迭代器:遍历容器中数据的对象。对存储于容器中的数据进行处理时,迭代器能从一个成员移向另一个成员。他能按预先定义的顺序在某些容器中的成员间移动。对普通的一维数组、向量、双端队列和列表来说,迭代器是一种指针。

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

容器(container):容器是数据在内存中组织的方法,例如,数组、堆栈、队列、链表或二叉树(不过这些都不是STL标准容器)。STL中的容器是一种存储T(Template)类型值的有限集合的数据结构,容器的内部实现一般是类。这些值可以是对象本身,如果数据类型T代表的是Class的话。

算法(algorithm):算法是应用在容器上以各种方法处理其内容的行为或功能。例如,有对容器内容排序、复制、检索和合并的算法。在STL中,算法是由模板函数表现的。这些函数不是容器类的成员函数。相反,它们是独立的函数。令人吃惊的特点之一就是其算法如此通用。不仅可以将其用于STL容器,而且可以用于普通的C++数组或任何其他应用程序指定的容器。

迭代器(iterator):一旦选定一种容器类型和数据行为(算法),那么剩下唯一要他做的就是用迭代器使其相互作用。可以把达代器看作一个指向容器中元素的普通指针。可以如递增一个指针那样递增迭代器,使其依次指向容器中每一个后继的元素。迭代器是STL的一个关键部分,因为它将算法和容器连在一起。

下面我将依次介绍STL的这三个主要组件。

1.       容器

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

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

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

u     基本容器——向量

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

· 默认构造函数,构造一个初始长度为0的空向量,如:vector<int> v1;

· 带有单个整形参数的构造函数,此参数描述了向量的初始大小。这个构造函数还有一个可选的参数,这是一个类型为T的实例,描述了各个向量种各成员的初始值;如:vector<int> v2(n,0); 如果预先定义了:n,他的成员值都被初始化为0;

· 复制构造函数,构造一个新的向量,作为已存在的向量的完全复制,如:vector<int> v3(v2);

· 带两个常量参数的构造函数,产生初始值为一个区间的向量。区间由一个半开区间[first,last) 来指定。如:vector<int> v4(first,last)

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

 1 //程序:初始化演示
 2 #include <cstring>
 3 #include <vector>
 4 #include <iostream>
 5 using namespace std;
 6 int ar[10] = { 12, 45, 234, 64, 12, 35, 63, 23, 12, 55 };
 7 char* str = "Hello World";
 8 int main()
 9 {
10     vector <int> vec1(ar, ar+10);   //first=ar,last=ar+10,不包括ar+10
11     vector < char > vec2(str,str+strlen(str)); //first=str,last= str+strlen(str),
12     cout<<"vec1:"<<endl;  
13     //打印vec1和vec2,const_iterator是迭代器,后面会讲到
14     //当然,也可以用for (int i=0; i<vec1.size(); i++)cout << vec[i];输出
15     //size()是vector的一个成员函数
16     for(vector<int>::const_iterator p=vec1.begin();p!=vec1.end(); ++p)
17         cout<<*p;
18         cout<<'\n'<<"vec2:"<<endl;
19     for(vector< char >::const_iterator p1=vec2.begin();p1!=vec2.end(); ++p1)
20         cout<<*p1;
21     cout<<'\n';
22     return 0;
23 }     

为了帮助理解向量的概念这里写了一个小例子其中用到了vector的成员函数:begin(),end(),push_back(),assign(),front(),back(),erase(),empty(),at(),size()

 1 #include <iostream>
 2 #include <vector>
 3 using namespace std;
 4 typedef vector<int> INTVECTOR;//自定义类型INTVECTOR
 5 //测试vector容器的功能
 6 int main()
 7 {
 8     //vec1对象初始为空
 9     INTVECTOR vec1;  
10     //vec2对象最初有10个值为6的元素
11     INTVECTOR vec2(10,6);
12     //vec3对象最初有3个值为6的元素,拷贝构造
13     INTVECTOR vec3(vec2.begin(),vec2.begin()+3);
14     //声明一个名为i的双向迭代器
15     INTVECTOR::iterator i;
16     //从前向后显示vec1中的数据
17     cout<<"vec1.begin()--vec1.end():"<<endl;
18     for (i =vec1.begin(); i !=vec1.end(); ++i)
19         cout << *i << " ";
20     cout << endl;
21     //从前向后显示vec2中的数据
22     cout<<"vec2.begin()--vec2.end():"<<endl;
23     for (i =vec2.begin(); i !=vec2.end(); ++i)
24         cout << *i << " ";
25     cout << endl;
26     //从前向后显示vec3中的数据
27     cout<<"vec3.begin()--vec3.end():"<<endl;
28     for (i =vec3.begin(); i !=vec3.end(); ++i)
29         cout << *i << " ";
30     cout << endl;
31     //测试添加和插入成员函数,vector不支持从前插入
32    vec1.push_back(2);//从后面添加一个成员
33     vec1.push_back(4);
34     vec1.insert(vec1.begin()+1,5);//在vec1第一个的位置上插入成员5
35     //从vec1第一的位置开始插入vec3的所有成员
36     vec1.insert(vec1.begin()+1,vec3.begin(),vec3.end());
37     cout<<"after push() and insert() now the vec1 is:" <<endl;
38     for (i =vec1.begin(); i !=vec1.end(); ++i)
39         cout << *i << " ";
40     cout << endl;
41     //测试赋值成员函数
42     vec2.assign(8,1);   // 重新给vec2赋值,8个成员的初始值都为1
43     cout<<"vec2.assign(8,1):" <<endl;
44     for (i =vec2.begin(); i !=vec2.end(); ++i)
45         cout << *i << " ";
46     cout << endl;
47     //测试引用类函数
48     cout<<"vec1.front()="<<vec1.front()<<endl;//vec1第零个成员
49     cout<<"vec1.back()="<<vec1.back()<<endl;//vec1的最后一个成员
50     cout<<"vec1.at(4)="<<vec1.at(4)<<endl;//vec1的第五个成员
51     cout<<"vec1[4]="<<vec1[4]<<endl;
52    //测试移出和删除
53     vec1.pop_back();//将最后一个成员移出vec1
54     vec1.erase(vec1.begin()+1,vec1.end()-2);//删除成员
55     cout<<"vec1.pop_back() and vec1.erase():" <<endl;
56     for (i =vec1.begin(); i !=vec1.end(); ++i)
57         cout << *i << " ";
58     cout << endl;
59     //显示序列的状态信息
60     cout<<"vec1.size(): "<<vec1.size()<<endl;//打印成员个数
61     cout<<"vec1.empty(): "<<vec1.empty()<<endl;//清空
62 }

push_back()是将数据放入vector(向量)或deque(双端队列)的标准函数。Insert()是一个与之类似的函数,然而它在所有容器中都可以使用,但是用法更加复杂。end()实际上是取末尾加一,以便让循环正确运行--它返回的指针指向最靠近数组界限的数据。

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

u     双端队列(qeque容器类):

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

 1 #include <iostream>
 2 #include <deque>
 3 using namespace std;
 4 typedef deque<int> INTDEQUE;//有些人很讨厌这种定义法,呵呵
 5 //从前向后显示deque队列的全部元素
 6 void put_deque(INTDEQUE deque, char *name)
 7 {
 8     INTDEQUE::iterator pdeque;//仍然使用迭代器输出
 9     cout << "The contents of " << name << " : ";
10     for(pdeque = deque.begin(); pdeque != deque.end(); pdeque++)
11         cout << *pdeque << " ";//注意有 "*"号哦,没有"*"号的话会报错
12     cout<<endl;
13 }
14 //测试deqtor容器的功能
15 int main()
16 {
17     //deq1对象初始为空
18     INTDEQUE deq1;  
19     //deq2对象最初有10个值为6的元素
20     INTDEQUE deq2(10,6);
21     //声明一个名为i的双向迭代器变量
22     INTDEQUE::iterator i;
23     //从前向后显示deq1中的数据
24     put_deque(deq1,"deq1");
25     //从前向后显示deq2中的数据
26     put_deque(deq2,"deq2");
27     //从deq1序列后面添加两个元素
28     deq1.push_back(2);
29     deq1.push_back(4);
30     cout<<"deq1.push_back(2) and deq1.push_back(4):"<<endl;
31     put_deque(deq1,"deq1");
32     //从deq1序列前面添加两个元素
33     deq1.push_front(5);
34     deq1.push_front(7);
35     cout<<"deq1.push_front(5) and deq1.push_front(7):"<<endl;
36     put_deque(deq1,"deq1");
37     //在deq1序列中间插入数据
38     deq1.insert(deq1.begin()+1,3,9);
39     cout<<"deq1.insert(deq1.begin()+1,3,9):"<<endl;
40     put_deque(deq1,"deq1");
41     //测试引用类函数
42     cout<<"deq1.at(4)="<<deq1.at(4)<<endl;
43     cout<<"deq1[4]="<<deq1[4]<<endl;
44     deq1.at(1)=10;
45     deq1[2]=12;
46     cout<<"deq1.at(1)=10 and deq1[2]=12 :"<<endl;
47     put_deque(deq1,"deq1");
48     //从deq1序列的前后各移去一个元素
49     deq1.pop_front();
50     deq1.pop_back();
51     cout<<"deq1.pop_front() and deq1.pop_back():"<<endl;
52     put_deque(deq1,"deq1");
53     //清除deq1中的第2个元素
54     deq1.erase(deq1.begin()+1);
55     cout<<"deq1.erase(deq1.begin()+1):"<<endl;
56     put_deque(deq1,"deq1");
57     //对deq2赋值并显示
58     deq2.assign(8,1);
59     cout<<"deq2.assign(8,1):"<<endl;
60     put_deque(deq2,"deq2");
61 }

上面我们演示了deque如何进行插入删除等操作,像erase(),assign()是大多数容器都有的操作。关于deque的其他操作请参阅其他书籍。

u     表(List容器类)

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

  同时,list仍然包涵了erase(),begin(),end(),insert(),push_back(),push_front()这些基本函数,下面我们来演示一下list的其他函数功能。merge():合并两个排序列表;splice():拼接两个列表;sort():列表的排序。

 

 1 #include <iostream>
 2 #include <string>
 3 #include <list>
 4 using namespace std;
 5 void PrintIt(list<int> n)
 6 {
 7     for(list<int>::iterator iter=n.begin(); iter!=n.end(); ++iter)
 8       cout<<*iter<<" ";//用迭代器进行输出循环
 9 }
10    
11 int main()
12 {
13     list<int> listn1,listn2;    //给listn1,listn2初始化
14     listn1.push_back(123);
15     listn1.push_back(0);
16     listn1.push_back(34);
17     listn1.push_back(1123);    //now listn1:123,0,34,1123
18     listn2.push_back(100);
19     listn2.push_back(12);    //now listn2:12,100
20     listn1.sort();
21     listn2.sort();    //给listn1和listn2排序
22     //now listn1:0,34,123,1123         listn2:12,100
23     PrintIt(listn1);
24     cout<<endl;
25     PrintIt(listn2);
26     listn1.merge(listn2);    //合并两个排序列表后,listn1:0,12,34,100,123,1123
27     cout<<endl;
28     PrintIt(listn1);
29 }

queue Functions

Illustrates how to use the queue::push, queue::pop, queue::empty, queue::back, queue::front, and queue::size Standard Template Library (STL) functions in Visual C++.

queue::push( );
queue::pop( );
queue::empty( );
queue::back( );
queue::front( );
queue::size( );

Note   The class/parameter names in the prototype do not match the version in the header file. Some have been modified to improve readability.

Description

The sample shows the queue implementation using list and deque containers.

Example

 

 1 // queue.cpp
 2 // compile with: /EHsc
 3 //
 4 // Functions:
 5 //    queue::push(), queue::pop(), queue::empty(), queue::back(),
 6 //    queue::front(),queue::size()
 7 
 8 #include <list>
 9 #include <iostream>
10 #include <queue>
11 #include <deque>
12 
13 using namespace std ;
14 
15 // Using queue with list
16 typedef list<int > INTLIST;
17 typedef queue<int>  INTQUEUE;
18 
19 // Using queue with deque
20 typedef deque<char*> CHARDEQUE;
21 typedef queue<char*> CHARQUEUE;
22 
23 int main(void)
24 {
25     size_t size_q;
26     INTQUEUE q;
27     CHARQUEUE p;
28 
29     // Insert items in the queue(uses list)
30     q.push(42);
31     q.push(100);
32     q.push(49);
33     q.push(201);
34 
35     // Output the size of queue
36     size_q = q.size();
37     cout << "size of q is:" << size_q << endl;
38 
39     // Output items in queue using front()
40     // and use pop() to get to next item until
41     // queue is empty
42     while (!q.empty())
43     {
44         cout << q.front() << endl;
45         q.pop();
46     }
47 
48 // Insert items in the queue(uses deque)
49     p.push("cat");
50     p.push("ape");
51     p.push("dog");
52     p.push("mouse");
53     p.push("horse");
54 
55     // Output the item inserted last using back()
56     cout << p.back() << endl;
57 
58     // Output the size of queue
59     size_q = p.size();
60     cout << "size of p is:" << size_q << endl;
61 
62     // Output items in queue using front()
63     // and use pop() to get to next item until
64     // queue is empty
65     while (!p.empty())
66     {
67         cout << p.front() << endl;
68         p.pop();
69     }
70 }
Output
size of q is:4
42
100
49
201
horse
size of p is:5
cat
ape
dog
mouse
horse

 

转载于:https://www.cnblogs.com/zhlechn/p/3229336.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值