C++中顺序容器及容器适配器大纲



本章主要围绕着vector,deque,list讲解,分析它们的不同之处和相同的地方以及使用方法。在此基础上,总结出了string容器的使用。并且讲述了适配器的使用。


1、顺序容器的定义和初始化


顺序容器定于了相同类型的数据元素的集合,里面的元素是在一块连续的内存空间中按添加的次序排序的,而不是按值得大小排序的,因此叫做顺序容器。C++中顺序容器主要有三种,vector,list,deque。这三种容器的区别在与访问,添加,删除元素的运行效率和代价不同,但如果两个容器执行相同的操作,那么它们的接口函数是一样的。顺序容器的定义和初始化方式有以下几种:





根据上图的内容,考虑一下几种情况:

vector<int> vect;
list<string> sList;
deque<double> doubleDeque;

上面三条语句都只是定义了以容器对象,并调用了容器的默认构造函数对容器进行初始化,这是最高效的使用容器的一种方式,但是注意这种方法得到的容器是空的容器,即容器不包含任何数据。


vector<int> vect;
vector<int> vect1(vect);
vector<string> vect2(vect;)
list<int> vect 3(vect);

上面四条语句中,目的是将vect里面的元素内容复制给另一个容器,对于可复制的对象,必须得失相同容器类型并且容器含有的数据类型也应该相同的对象。因此上面只有vect1是可以成功复制的,vect2,vect3都是不行的。


vector<int> vect;
.......//给vect添加数据
vector<int>::iterator begin=vect.begin();
vector<int>::iterator end=vect.end();

vector<int> vect1(begin,end);
list<int> intList<begin,end);


上述语句通过迭代器将一个容器的内容复制到另外一个容器里面,只要容器的数据类型是可以兼容的就可以复制成功。


vector<int> vect(10,10)
vector<int> vect(10)

第一句将vect初始化为含有10个值为10的元素,第二句则是10个值为0的元素。上面两种方式只适用于顺序容器,并且包含的数据类型,如果不显式指定初始化值,就必须含有默认构造函数。





2、顺序容器的类型约束


对于可应用于容器的数据类型来说,必须满足一下两点:

1、数据类型必须可复制。
2、数据类型必须支持赋值运算。

因为引用类型不可以复制,所以不能作为容器的数据类型,同样的I/O标准库的类型也不支持,所以不能作为容器的数据类型。除此之外,其余的元素类型都可以作为容器的数据类型。容器也可以作为容器的数据类型,比如:

vector< vector<double> >vect;

对于包含容器的容器类型,只有一个要求就是> >必须含有一个空格以区分输入操作符。




3、顺序容器的迭代器支持的操作


在了解这个之前,先说明,vector,deque的存储结构是顺序存储结构,而list是链式存储结构,因此,它们的迭代器所支持的操作是基于这些结构来定义的。同时我们要知道,容器提供的迭代器,只要它们执行的操作是一样的那么它们的操作方法就一样。比如对vector的迭代器进行解引用操作和得出数据元素的值,对deque进行解引用操作也是一样的。下面了解一下,vector,list,deque都支持的迭代器操作:





然后是仅支持vector,deque的操作:





要区分容器支持什么操作,就要结合他们的存储结构来分析。顺序存储结构支持随机读取,因此对迭代器进行基本运算和比较运算都是可以的。所以vector,deque的迭代器支持上述所有的操作,list由于不支持随机访问,所以只能进行相邻元素的比较和计算。



3.1 迭代器的范围


迭代器的范围包含了第一个元素一直到最后一个元素的下一个元素之间。因此当begin和end不想等的时候,说明容器至少包含一个元素,当迭代器未通过自增操作而等于end的时候,说明容器是一个空容器。当迭代器通过自增操作等于end的时候,说明已经达到了容器的末尾。



4、顺序容器作为前缀的类型别名


每种容器类型都包含了许多的类型别名,比如我们使用过的vector<int>::iterator就是一种,下面列出所哟容器都有的别名。



这里只讲特殊的地方,reverse_iterator是逆序读取元素,即对它进行自增操作等同于读取它的前一个元素。而value_type可以在我们不知道具体数据类型的情况下,对元素进行操作,reference即元素类型的一个引用,和一般的引用一样。



4.1迭代器的起始终止元素

考虑一下几个方法:





上述可以获取顺序读取或者逆序读取的迭代器,每种迭代器都可以是一个含有const的只读迭代器。比如reverse_iterator有只读的const_reverse_iterator。




5、对顺序容器插入元素


容器定义了插入元素的相关操作:






这里只提醒,在对容器进行插入删除元素,会导致原来指向容器的迭代器失效,因此,如果执行了改变容器状态的操作,必须重新计算迭代器的值。下面给出基本的添加元素的实例:


list<string> sList;
	//在容器后端添加元素
	sList.push_back("hello");
	//在容器前端添加元素,此方法只能用于list或者deque
	sList.push_front("hi,");

	string s[] = { "my", "name", "is", "HuangYi" };

	//在表头插入整段元素

	sList.insert(sList.begin(), s, s + 4);

	//在表头插入几个相同的单词

	sList.insert(sList.begin(),5, "begin");

	//注意,在对容器进行插入或删除操作之后,会导致这个容器的内容重新加载,即如果保存了容器之前的迭代器变量,那么这些变量会失效,会指向一个不定值。
	//因此,对它们执行的操作是没有定义的。

	for (list<string>::iterator iter = sList.begin(); iter != sList.end(); iter++)
	{
		cout << (*iter)<<" ";
	}
	cout << endl;

	system("pause");

运行效果:






6、容器容量的相关操作


对于容器的容量,可以读取也可以重新设置以适应新的数据。如下:





这里主要讲resize,如果n的值比原来的size要打,则在容器后面添加元素,所添加的元素可以显示指定值,也可以不指定从而调用默认构造函数。如果n的值比size小,则在第n-1之后的元素都会被抛弃掉。因为resize会改变容器的状态,因此,迭代器需要重新获取。



7、访问容器内的元素



除了通过迭代器获取的方式之外,还可以使用以下几种方式访问容器内元素:






注意back返回的是最后一个元素的引用,front返回的是第一个元素的引用。考虑下面的语句:

list<int>::const_refrence ref1=*(c.begin());
list<int>::const_refrence ref2=c.front();
list<int>::const_refrence ref3=*(--c.end());
list<int>::const_refrence ref4=c.back();

其中ref1和ref2相等,ref3和ref4相等。注意c.end()返回的是指向末端的下一个地址,所以必须向减去1在进行解引用操作。如果希望通过随机访问读取元素,只能是顺序存储结构的vector和deque。



8、删除容器内元素


C++对容器同样提供了删除操作,包括删除某个元素,删除某段元素,或者删除头元素或者结尾元素。看下图:





删除头元素和结尾元素。

list<int> intList;

if(!intList.empty())
{
process(intList.front());//对头节点进行处理
process(intList.back());//对结尾元素进行处理
intList.pop_back();//删除结尾元素
intList.pop_front();//删除头元素,不适用与vector
}

我们可以解除find函数来查找某个迭代器范围内指定元素的迭代器,这样就可以删除指定元素了。

list<int>::iterator iter=find(intList.begin(),intList.end(),searchValue);
intList.erase(iter);//需要判断是否是结尾if(iter!=intList.end());

find函数接收两个指定查找范围的迭代器以及一个要查找的值,如果找到了就返回指向该值的迭代器,否则返回超出末端的下一个位置的迭代器。






9、通过swap操作交换容器的值


大部分操作都可以使用erase和insert方法代替,在C++中,对容器的赋值操作,除了在初始化方面进行赋值,还可以调用assign或者swap方法。但是它们的实现方式是不一样的。assign方法会首先擦除容器本身的所有元素,然后再将指定内容复制进去,所以原本的迭代器会失效。swap方法是将两个容器的内容互相交换,原本的迭代器不会失效,因为swap操作只是交换内容而不会进行任何插入删除操作。由于以上两个原因,assign可以使用任何可兼容的数据元素之间的赋值,swap则必须要求交换的容器类型和数据类型都相同。常用的方法如下:







10、Vector的自增长策略


每次我们往容器中增加元素的时候,容器(仅vector)都会自增长从而提高我们操作容器的效率。其中size获取容器当前保存的数量,capacity获取容器当前可保存的最大数量,此值会随着我们呢添加元素而增长,reverse可以自己设置最大容量,一旦设置了容量大小,随后我们进行添加元素的时候就不会动态增加预留空间了,除非添加的元素数量达到了我们设定的大小。看例子:


	vector<int> vect;
	//内存分配策略只会在容器容量添加了元素的时候才会动态分配,此时没有元素,所以容量和数量都是0
	cout << "size:" << vect.size() << ";capacity:" << vect.capacity() << endl;
	//添加是个元素,可以发现由于空间分配策略,使得容量和数量并不相等
	for (int i = 0; i < 10; i++)
	{
		vect.push_back(i);
	}
	cout << "after insert 10 elements;size:" << vect.size() << ";capacity:" << vect.capacity() << endl;

	//如果不想使用系统自定义的分配策略,可以自己指定预留的空间
	vect.reserve(50);
	//此时插入元素,只要没有达到容器的空间极限,就不会动态增加预留空间
	for (int i = 10; i < 50; i++)
	{
		vect.push_back(i);
	}
	cout << "after insert 10-48 value;size:" << vect.size() << ";capacity:" << vect.capacity() << endl;
	//一旦达到极限,就会动态申请空间
	vect.push_back(50);
	cout << "after insert 49 value;size:" << vect.size() << ";capacity:" << vect.capacity() << endl;

运行结果:








11、容器的选择


应该如何选择vector,list,deque呢。主要结合一下特征考虑。

①、随机访问的操作频繁吗?
②、插入位置是随机的概率大吗?

在给出答案之前,要知道,vector和deque是顺序存储的,因此它们的随机访问效率很高,list是链式存储的,访问中间元素需要遍历,代价很大。但同时意味着,如果向中间插入元素,vector和deque由于需要移动元素,都会话费很大的时间,而list却很方便。综上所属:

vector适应于频繁随机访问的而插入操作大多发生在表尾的操作情况。
list适用于访问头元素或结尾元素多,插入操作随机的情况。
deque适用于频繁随机访问,但可能需要在表头进行插入操作的情况。




12、string支持的操作


string可以看作一种特殊的容器,一种只包含字符的容器。下面列出是string支持的操作,就不做解释了,不懂可参考前面的内容。基本一样。


基本操作






构造函数





添加删除重置











子串






替换添加







查找







比较






13、容器适配器stack,queue,priority_queue


C++中提供了一组适配器,可以让你在使用顺序容器的时候,可以用适配器的使用方式来使用。适配器包含在stack,queue头文件中。适配器的初始化,可以接受一个容器作为数据元素

stack<int> sta(deq)

默认情况下,stack,queue的默认容器是deque,priority_queue是vector。如果要覆盖默认适配器,可以这样子:

queue<string,vector<string> > strQueue(deq);

stack适配器支持的操作如下:




队列和优先级队列的区别,队列添加的元素总是在最后面,优先级队列则是按照优先级进行排序的。队列支持的操作







---------文章写自:HyHarden---------

--------博客地址:http://blog.csdn.net/qq_25722767-----------




  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值