您的位置:首页 > 编程语言 > C语言/C++

C++学习笔记(五)标准模板库STL

2015-08-30 16:41 579 查看

一、概述

STL,即是标准模板库(Standard Template Library)。定义了强大的,基于模板的类库,实现了许多通用的数据结构及处理这些结构的算法。本文对STL的三个关键内容——容器(container)、迭代器(iterator)和算法(algorithm)进行介绍。下面对这三部分进行简单介绍,因为3者的联系密切

(1)STL容器

容器,是一种可以用来存放多个数据且长度动态变化的结构。STL的容器可分为三类:序列容器、关联容器和容器适配器,

(a)具体分类如下



序列容器描述了线性数据结构,关联容器描述了非线性数据结构,两者统称为首类容器。栈和队列是受限制的链表结构,因此将其作为容器适配器。除了这三种,还有4种“近容器”:C类型的基于指针的数组、字符串(string类)、存放标志位的bitset类、用于进行高速向量运算的valarray类。它们被称为“近容器”,一方面是它们的功能与首类容器很像,但是不支持所有首类容器的功能。

(b)STL容器的通用函数

所有的STL容器都提供了相近的功能。以下为所有标准库容器中通用的函数(但容器适配器priority_queue)没提供重载的“<”、“<=”、“>”、“>=”、“==”、“!=”)。

默认构造函数:对容器进行默认初始化的构造函数

拷贝构造函数:用同类型已有容器的副本初始化容器的构造函数

empty:容器没有元素则返回true,否则为false.

size:返回当前容器中的元素个数

运算符(=、>、>=、<、<=、==、!=):当满足比较的条件时返回true,否则返回fals

swap:交换两个容器中的元素

只使用于首类容器(序列容器和关联容器的函数)的函数

max_size:返回一个容器的可装最大元素个数

begin:返回引用容器中的第一个元素的iterator或const_iterator

end:返回引用容器末端之后位置的iterator或const_iterator

rbegin:返回引用容器末端位置的reverse_ iterator或const_reverse_iterator

rend:返回引用容器第一个元素之前位置的reverse_ iterator或const_reverse_iterator

erase:用于删除容器中的一个或多个元素

clear:删除容器中的所有元素

(2)迭代器

迭代器用于指向首类容器中的元素的一个模板类,每种类型所拥有的迭代器功能不一定相同。迭代器使用成员函数begin和end,已经自增自减操作访问容器中的元素。指向一个可修改元素的容器时使用iterator,指向不可修改元素的容器时使用const_iterator

(a)迭代器的类型和层次

以下为STL中迭代器的各种类型以及迭代器层次划分,由“弱”至“强”,每种类型都提供了一些特定的功能。这又决定了这种容器能否在指定的STL算法中使用。

输入迭代器(inputiteratot):用于从容器中读取元素。输入迭代器每次只能向前移动一个元素;只支持一遍扫描算法;不能使用相同的输入迭代器两次遍历一个序列容器如istream_iterator模板类

输出迭代器(output iteratot):用于将元素写入容器。输出迭代器每次只能向前移动一个元素;只支持一遍扫描算法;不能使用相同的输出迭代器两次遍历一个序列容器, 如ostream_iterator模板类

正向迭代器(forwarditerator):综合输入和输出迭代器的功能,并能保持它们在容器的位置

双向迭代器(bidirectionaliterator):在正向迭代器基础上增加了向后移动的功能,支持多遍扫描算法

随机访问迭代器(randomaccess iterator):在双向迭代器基础上增加了直接访问容器中任意元素的功能,即可以向前或向后跳转任意个元素



首类容器中vector、deque支持随机访问迭代器,list、set 、multiset 、map 、multimap支持双向迭代器,容器适配器(stack、queue、priority_queue)不支持迭代器,故也即首类容器可以使用迭代器遍历。

(b)迭代器的操作

适用于所有迭代器的操作:++p p++

输入迭代器:*p p = p1 p == p1 p != p1

输出迭代器:*p p = p1

正向迭代器:*p p = p1 p == p1 p != p1

双向迭代器:--p p--

随机访问迭代器:p +=i p -= i p + i p – i p[ i ] p < p1 p < =p1 p >p1 p >= p1

(3)算法

下面表示了STL包含了大约70个标准算法,例如插入、删除、搜索、排序等,很多算法的返回值是迭代器,是算法的结果。算法对迭代器的要求不一定相同,要求使用功能较强迭代器的算法则不能支持那些使用功能低迭代器的容器,而要求使用功能较弱迭代器的算法则可以支持那些使用功能强迭代器的容器



变序算法可以修改所作用的容器,非变序算法不会修改所作用的容器

二、序列容器

STL提供了三种序列容器,分别是vector、deque、list,其中vector和deque模板类是基于数组的,而list模板类是实现了一个链接的表数据结构。
(1)vector模板类
Vector是STL中最常用的容器之一,其结构与数组类似,但是具有比数组更优越特点,vector可以动态改变长度,且可以相互赋值,vector在尾部进行插入操作是高效的,但在中间插入或删除元素是低效的,因为操作之后整个部分都需要移动,vector的元素在内存中占用的是连续的空间。vector支持随机访问迭代器,除了可以使用通用函数之外,还有front、back、push_back、pop_back等函数。
以下举例说明了vector模板类的几个函数,这些函数大部分可用于首类容器
<span style="font-size:12px;">//testVector.cpp
#include <iostream>
using std::cout;
using std::endl;

#include <vector>       //使用vector模板类需包含头文件<vector>
using std::vector;      //需使用std::vector

template <typename T>
void printVctor( const vector<T> &integer); //模板函数,输出vector容器的元素

int main()
{
	vector< int > intVector;
	cout<<"The initial size of intVector is: "<<intVector.size()   //输出容器初始大小
		<<"\nThe initial capacity of intVector is: "<<intVector.capacity();//输出容器可存储大小

	intVector.push_back(1);
	intVector.push_back(2);
	intVector.push_back(3);

	cout<<"\nThe size of intVector is: "<<intVector.size()   //输出容器当前大小
		<<"\nThe capacity of intVector is: "<<intVector.capacity();//输出容器在下次分配内存前容量大小

	cout<<"\nOutput vector using iterator: ";
	printVctor(intVector);
	cout<<"\nReversed contents of intVector: ";

	vector<int>::const_reverse_iterator reverseIterator;
	vector<int>::const_reverse_iterator endingOfIterator = intVector.rend();

	for (reverseIterator = intVector.rbegin();  //反向迭代器向后遍历
			reverseIterator != endingOfIterator;
			++reverseIterator)
		cout<<*reverseIterator<<' ';

	cout<<endl;
	return 0;
}

template <typename T>
void printVctor( const vector<T> &integer)
{
	typename vector<T>::const_iterator constIterator;

	for( constIterator = integer.begin() ;  //迭代器前向遍历
		constIterator != integer.end() ; ++constIterator )
		cout<<*constIterator<<' ';
}
</span>
运行结果



程序中size()和capacit()分别表示了容器当前存放的元素长度和容器在下次分配内存之前所能存放元素的长度,容器始终capacit >= size;可用成员函数resize()设容器当前长度,元素不够时会进行补0,用reserve()设置capacity;置接着分别使用了 const_iterator和 const_reverse_iterator进行向前和向后读取容器中元素。

Vector的元素操纵函数如下所示

//testVector2.cpp
#include <iostream>
using std::cout;
using std::endl;

#include <vector>       //使用vector模板类需包含头文件<vector>
using std::vector;       //vector包含在std里
#include <algorithm>    //使用了copy()函数,需包含此头文件
#include <iterator>     //使用ostream_iterator模板类,需包含此头文件
#include <stdexcept>    //检查是否发生越界错误,需包含此头文件

int main()
{
	int a[6] ={ 1,2,3,4,5,6};
	vector<int > intVector(a ,a+6); //vector的构造函数,将一定长度的数组中元素赋给容器
	std::ostream_iterator<int> output( cout ," ");  //ostrame_iterator输出迭代器
	cout<<"intVector contains: ";
	std::copy(intVector.begin() ,intVector.end(),output);  //从容器从首至末输出元素,间隔‘ ’

	cout<<"\nFirst element of intVector: "<<intVector.front() //取容器首元素
		<<"\nLast elemennt of intVector: "<<intVector.back(); //取容器末元素

	intVector[0] = 8;
	intVector.at( 2) =10;

	intVector.insert(intVector.begin()+1 ,33);    //在容器第2位插入一个元素

	cout<<"\n\nContents of intVector after changes: ";
	std::copy(intVector.begin() ,intVector.end(),output);
	
	try
	{
		intVector.at(99) = 666;      
	}
	catch( std::out_of_range outOfRange)  //捕捉到异常
	{
		cout<<"\n\nError: "<<outOfRange.what(); //输出错误类型
	}

	intVector.erase(intVector.begin()); //删除首元素
	cout<<"\n\nContents of intVector after erasing first element: ";
	std::copy(intVector.begin() ,intVector.end(),output);

	intVector.erase(intVector.begin() ,intVector.end());   //从容器首至末删除元素
	cout<<"\n\nAftering erasing all elements, intVector "
		<<(intVector.empty()? "is":"is not")<<" empty"<<endl;

	intVector.insert(intVector.begin(),a ,a+5);    //将数组中的5个元素依次插入容器
	cout<<"\n\nContains of intVector before clear: ";
	std::copy(intVector.begin() ,intVector.end(),output);

	intVector.clear();  //清除容器中的元素
	cout<<"\nAfter cleat ,intVector "
		<<(intVector.empty()? "is":"is not")<<" empty"<<endl;

	return 0;
}


运行结果



STL中异常的类型除了out_of_range(表示下标超出范围),还有invalid_argument(表示传递给函数的参数无效),length_error(表示试图创建过长的容器、字符串等),bad_alloc(试图使用new分配内存,但可用内存不足,操作失败)

(2)list模板类

list序列容器可以在容器的任一位置有效地进行插入和删除操作,list模板类实现了一个双向链表——list容器中的每个节点均含有一个指向前一个节点和一个指向后一个节点的指针list支持双向迭代器,除了支持通用函数外,还提供了9个其他的函数:splice、push_front 、pop_front、remove、remove_if、unique、merge、reverse和sort。testVector.cpp和testVector2.cpp中的许多函数可用于list,示例如下:

//testList.cpp
#include <iostream>
using std::cout;
using std::endl;

#include <list>  //使用list需包含此头文件
using std::list;  //如果不用,则用list时需std::list
#include <algorithm>  //STL算法头文件
#include <iterator>   //使用ostream_iterator

template <typename T>
void printList(const list<T> &listRef);

int main()
{
	int a[4] = { 2 ,4 ,6 ,8};
	list<int > firstList;
	list<int > secondList;

	firstList.push_front(1);   //在前面插入1
	firstList.push_front(2);
	firstList.push_back(4);    //在后面插入4
	firstList.push_back(3);

	cout<<"firstList contains: ";
	printList(firstList);

	firstList.sort();         //将链表中元素排序
	cout<<"\nfirstList after sorting contains: ";
	printList(firstList);

	secondList.insert(secondList.begin() ,a ,a+4);   //在链表前面插入数组中的4个元素
	cout<<"\nAfter insert,secondList contains: ";
	printList(secondList);

	firstList.splice(firstList.end() ,secondList);  //在末端依次插入secondList中的元素,删除secondList中的元素
	cout<<"\nAfter splice,firstList contains: ";
	printList(firstList);

	firstList.sort();           //排序
	cout<<"\nAfter sort,firstList contains: ";
	printList(firstList);

	secondList.insert(secondList.begin(),a ,a+4);  //在链表的末尾依次插入数组的4个元素
	secondList.sort();           //排序
	cout<<"\nAfter insert,secondList contains: ";
	printList(secondList);

	firstList.merge(secondList);         //把secondList中元素按顺序插入firstList,并删除secondList中的元素
	cout<<"\nAfter merge:\nfirstList contains: ";
	printList(firstList);
	cout<<"\nsecondList contains: ";
	printList(secondList); //已经为空

	firstList.pop_front();//删除首部元素
	firstList.pop_back();  //删除末端元素
	cout<<"\nAfter pop_front and pop_back:\nfirstist contains: ";
	printList(firstList);

	firstList.unique();   //将相同的元素保留一个,其余删除
	cout<<"\nAfter unique,firstList contains: ";
	printList(firstList);

	firstList.swap(secondList); //交换两个容器的内容
	cout<<"\nAfter swap:\nfirstList contains: ";
	printList(firstList);
	cout<<"\nsecondList contains: ";
	printList(secondList);

	firstList.assign(secondList.begin() ,secondList.end());//将两个迭代器指定范围的secondList中的元素代替fristList中的元素
	cout<<"\nAfter assign,firstList contains: ";
	printList(firstList);

	firstList.merge(secondList); //把secondList中元素按顺序插入firstList,并删除secondList中的元素
	cout<<"\nAfter merge,firstList contains: ";
	printList(firstList);

	firstList.remove(5); //将容器所有的元素4删除,若容器中没有此元素则不做修改
	cout<<"\nAfter remove(4),firstList contains: ";
	printList(firstList);
	cout<<endl;

	return 0;
}

template <typename T>
void printList(const list<T> &listRef)
{
	if(listRef.empty())
		cout<<"List is empty";
	else
	{
		std::ostream_iterator<T> output(cout ," ");
		std::copy(listRef.begin() ,listRef.end() ,output);  //将容器中元素从首至末依次输出,元素间隔“ ”
	}
}
运行结果



Push_front是list和deque特有的,vector不支持,而Push_back适用所有的首类容器

(3)deque模板类

deque具有vector和list的许多优点,deque相当于双向的队列,提供了在首部和尾部进行高效插入和删除操作的功能,而list可在中间进行高效插入和删除操作,deque支持随机访问迭代器,可以使用所有的STL算法。

//testDeque.cpp
#include <iostream>
using std::cout;
using std::endl;

#include <deque> //使用deque需包含此头文件
using std::deque;
#include <algorithm>
#include <iterator>

int main()
{
	deque<double> values; 
	std::ostream_iterator<double> outputs( cout ," ");

	values.push_front(1.1); //首部插入元素
	values.push_front(2.2);
	values.push_back(3.3);  //尾部插入元素

	cout<<"values contains: ";
	for (int i=0;i<values.size() ;i++)
		cout<<values[i] <<' ';  //使用下标访问元素

	values.pop_front(); //删除首部元素
	cout<<"\nAfter pop_front,values contains: ";
	std::copy(values.begin(),values.end() ,outputs);//使用输出迭代器输出元素

	values[1] = 5.5; //使用下标直接修改元素
	cout<<"\nAfter values[1] = 5.5,values contains: ";
	std::copy(values.begin(),values.end() ,outputs);
	cout<<endl;
	return 0;
}
运行结果



关联容器(multiset、set、multimap、map)及容器适配器( stack、queue、priority_queue)较为简单,详细资料可参见《cpp大学教程第5版》第23章
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: