c++之容器

本文详细介绍了C++标准库中的容器,包括容器的六大部件:容器、分配器、算法、迭代器、适配器和仿函数。重点讨论了sequence containers和associative containers,如vector、deque、list、forward_list、set、map等,并通过代码示例进行了性能测试,探讨了不同场景下选择哪种容器更高效。此外,还提到了异常处理中的try、catch、throw的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  •    简述: C++标准库中的代码几乎都是使用泛型编程(GP),而所谓的泛型编程:就是使用tmeplate为主要工具来编写程序。

  C++标准库( C++ standard library )
  对于STL标准模板库而言,standard template library标准库以hearderfile形式呈现,所以可以看到其内容。对于C++标准库:


  1).C++变准哭的hearder files 不带副档名(.h),例如#include


  2) .新式C hearder files 不带副档名(.h),例如#include


  3) .旧式 C hearder files(带有副档名 .h )任然可用,例如:#include<stdio.h>。


  新式hearders 内的组件封装在namespace “std”中使用时通过using namespace std;或using std::cout这种形式;对于旧式hearders 内的组件不封装于 namespace “std“中。


  下面给大家推荐几个学习C++的网站,可以用于C++stl库的查询:www.cplusplus.com;www.cppReference.com;gcc.gnu.org.


  • STL的六大部件(components)

  STL的六大部件包括:容器(Containers)、分配器(Allocators)、算法(Algorithms)、迭代器(Iterators)、适配器(Adapters)、仿函数(Functions)如下图所示:


这里写图片描述


  从图中可以看出容器是需要有分配器来支持的,从而帮容器分配内存,而迭代器它实际上是相当于一种泛化的指针,通过下面简单的一小段程序来简要的说明一个这六大部件。

#include<iostream>
#include<vector>
#include<functional>
#include<algorithm>
#include<iterator>
using namespace std;

int main()
{
int ai[] = { 1, 23, 43, 13, 56, 75, 66, 98, 14,23,23 };
vector<int, allocator<int>>vi(ai, ai + 11);
cout << count_if(vi.begin(),vi.end(),bind2nd(less<int>(),40)) << endl;//小于40的个数
cout << count_if(vi.begin(), vi.end(), not1(bind2nd(less<int>(), 40))) << endl;//大于等于40的元素个数
return 0;
}

  上面的一小段程序包括了所说的六大部件:


  使用了容器 vector,然后通过分配器allocator来给容器vector分配内存,而count_if()是算法表示如果满足条件则计数,vi.begin()和vi.end()则使用了迭代器,not1为函数适配器(function adaptor),bind2nh表示绑定第二参数(也是函数适配器的一种),最后的less()则为比大小的函数。这段程序运行的结果如下所示:


这里写图片描述


输出小于40的有6个数,大于40的有5个数。


  谈到了算法,谈到了容器,免不了会想到使用户哪一个,哪一个效率高,取决与数据的特性,根据特性选择适当的容器,这里面会牵扯到复杂度的问题(complexity,Big-oh)


  目前常见的Big-oh有下列几种形式:


这里写图片描述


  而刚才我们说到的使用哪一个,哪一个效率高的问题,以及本文的标题为几种容器的测试,那么接下来我们主要来说一下C++标准库中的几种容器,

这里写图片描述
  通过上图可以看出,在C++中的容器主要有两大类:sequence containers和associative containers两种,而unordered containers也是属于associative containers的一种。图中的“->”、“<-”代表指针。存内存的角度来说,vector容器的扩充是通过分配器进行扩充的,而Deque的扩充是一段一段的进行,链表list有两个指针,分别指向上一个和下一个结点,而Forward list这是一个单向的链表,每个结点只有一个指针并且其指向下一个结点,而不能指向上一个结点,同时list比Forward list要占内存,list都是通过指针串起来的,且每个元素带有两个指针。同时他们都属于sequence containers。


  那么如果我们的需求是要进行大量的查找的话,更多的时候Associative containers 的效率会比sequence containers的效率要高,对于关联型容器有:Set\MultiSet\Map\MultiMap然而对于set而言他的数据成员中key就是value,value就是key,而Map它的key和value是分开的(对于Map而言,它的一个数据成员由一个key和一个value组成);通常我们还会使用到哈希表(Harshtable)这种unordered containers也属于一种Associative containers它有unordered set\MultiSet、unordered Map\MultiMap。


  **注意:**如果选择的是Set,表示放的元素不能重复;如果选择的是Map,表示元素的key不能重复;但是如果选择的是MultiSet或者MultiMap的话则MultiSet的元素是可以重复的、MultiMap的key(键值)也是可以重复的。


  了解了又哪些容器之后,接下来我们来对这几种容器分别通过代码来进行分析,程序通过查找某一个long型元素long get_a_target_long(),或者string型字符串来进行测试。下面的代码为在一个命名空间中定义需要获取的long型元素函数、需要获取string型的元素函数、对long型元素进行大小比较的函数 int comparelongs(const void * a,const void *b)、对string型字符串进行大小比较的函数int comparestrings(const void * a, const void * b)…

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#define snprintf _snprintf
using namespace std;
namespace exp1{
	long get_a_target_long()
	{
		long target = 0;
		cout << "target(0~" << RAND_MAX << ")  ";
		cin >> target;
		return target;
	}

	string get_a_target_string()
	{
		long target = 0;
		char buf[10];
		cout << "target string(0~ " << RAND_MAX << ") ";
		cin >> target;
		snprintf(buf, 10, "%", target);//将 long 型的对象target 转化成字符串
		return string(buf);
	}

	int comparelongs(const void * a,const void *b)
	{
		if (*(long *)a > *(long *)b)
			return 1;
		else if (*(long *)a < *(long *)b)
			return -1;
		else
			return 0;
		//return (*(long *)a-*(long *)b);
	}

	int comparestrings(const void * a, const void * b)
	{
		if (*(string *)a>*(string *)b)
			return 1;
		else if (*(string *)a < *(string *)b)
			return -1;
		else 0;
	}

}

  上述程序中* (long *)a的意思为将指针a转化成long型指针,然后再取它的地址。


  •   Array数组的测试程序:
#include<array>
#include<cstdio>
#include<cstdlib>
#include<ctime>
using namespace std;

namespace test_array{
const long ASIZE = 1000000;
void test_array()
{
	cout << "\n test_array()......\n";
	array<long, ASIZE>c;
	clock_t timestate = clock();
	long target =exp1:: get_a_target_long();
	srand((int)time(NULL));//随机数种子

	for (long i = 0; i < ASIZE; i++)
	{
		c[i] = rand();
	}
	//打印出数组的相关信息
	cout << "milli-seconds of rand array c : " << (clock() - timestate) << endl;
	cout << "array.front() = " << c.front() << endl;
	cout << "array.back() = " << c.back() << endl;
	cout << "array.size() = " << c.size() << endl;
	cout << "array.data() = " << c.data() << endl;

	timestate = clock();
	::qsort(c.data(),ASIZE,sizeof(long),exp1::comparelongs);//快速排序
	long * PItem = (long *)bsearch(&target,c.data(),ASIZE,sizeof(long),exp1::comparelongs);
	cout << "qsort()+bsearch(),milli-seconds : " << (clock() - timestate) << endl;
	if (PItem != NULL)
		cout << "found : " << *PItem << endl;
	else
		cout << "not found!" << endl;
}

}

int main()
{
	test_array::test_array();
	system("pause");
	return 0;
}

测试结果如下:


这里写图片描述


  其中bsearch为二分查找法,注意在使用二分查找法之前需要排好序,qsort()为快速排序法。测试的结果123为通过控制台输入的需要查找的字符串“123”,程序从开始到现在用时27856ms,数组的第一个元素为11186,数组的最后一个元素为24423,数组的长度为10万,数组的首地址为0173DE20,通过快速排序法后进行二分法查找字符串123用时121ms。


  •   vector容器的测试程序:
#include<iostream>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<string>
#include<ctime>
using namespace std;

namespace test_vector{

	void test_vector(long & value){
		cout << "\n test_vector()........\n" << endl;
		vector<string>s;
		char buf[10];
		clock_t timestate = clock();
		srand((int)time(NULL));//随机数种子
		for (long i = 0; i < value; i++){
			try{
				snprintf(buf, 10, "%d", rand());//将随机生成的 long 型数据变成 string 类型的字符串
				//将每一个字符串 压入 s 中
				s.push_back(string(buf));//在尾部增加一个数据
			}
/*************************************************************************************************/
			catch (exception & p){//捕获并处理异常
				cout << "i = " << i << "  " << p.what() << endl;
				/*如果new杂调用分配器分配存储空间时出现错误(错误信息被保存了一下),就会catch到
				一个bad_alloc 类型的异常,其中的what()函数,就是提取这个错误的基本信息,*/
				abort();//程序终止
			}
		}

		cout << "milli-seconds of rand vector s : " << (clock() - timestate) << endl;
		cout << "vector s.size() = " << s.size() << endl;
		cout <<"vector s.front() = " << s.front() << endl;
		cout << "vector s.back() = " << s.back() << endl;
		cout << "vector s.max_size() = " << s.max_size() << endl;//得到vector最大可能是多少
		cout <<  "vector s.data() = " << s.data() << endl;//传回数组在内存里面的起点的地址
		cout << "vector s.capacity() = " << s.capacity() << endl;//capacity 表示当前vector分配的大小

		string target = exp1::get_a_target_string();
		{//测试循序查找法 fine 的效率
			timestate = clock();
			auto PItem = ::find(s.begin(), s.end(), target);//循序查找法,找target
			cout << "::find milli-seconds : " << (clock() - timestate) << endl;
			if (PItem != s.end())
				cout << "found the vector target: " << *PItem << endl;
			else
				cout << "not found vector target!" << endl;
		}
		//使用二分法进行查找需要的时间与循序查找法进行对比,但是需要注意的是,使用二分法查找时
		//首先需要对其进行排序,然后才能 bsearch .
		{
			timestate = clock();
			sort(s.begin(), s.end());/*************** 注意算法调用时他的参数个数  *****************/
			string * PItem = (string *)::bsearch(&target,s.data(),value,sizeof(string),exp1::comparestrings);
			
			cout << "sort()+bsearch(),milli-seconds : " << (clock() - timestate) << endl;
			if (PItem!=NULL)
				cout << "found the vector target: " << *PItem << endl;
			else
				cout << "not found vector target!" << endl;
		}
	}
}

int main()
{
	long value =500000;
	test_vector::test_vector(value);
	system("pause");
	return 0;
}

  测试结果如下:


这里写图片描述


  随机生成50万个元素,然后再50万个元素中分别使用::find()以及bsearch()进行查找123字符串。


  **注意:**1.在写测试程序的时候,将每一个小的测试程序放在一个namespace中,每个namespace中的任何定义,任何变量不会与其他namespace相冲突,而且在每个namespace的上头会有这个namespace所需要的头文件,头文件可以重复#include,因为其自身有保护机制(#ifndef…#define…#endif).


  **注意:**2.变量的申明在写正规程序的时候放在程序的开始。


  **注意:**3.vector的空间时两倍增长的,当放第一个元素时它占用的内存空间是1,再加上一个元素后占用的内存空间时2;此时若是再加上一个元素后,它此时的内存占用空间不是3而是4,因为他的内存空间的增长是两倍两倍的增长的。


  **注意:**4.在上述的程序大家会看到使用可try…catch…的写法,这样的话程序执行到这一块后如果发生了异常则会终止程序abort()。


  •   list容器的测试程序

#include<iostream>
#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<list>
#include<algorithm>
using namespace std;

namespace test_list{

	void test_list(long & value)
	{
		cout << "\n test_list()......\n" << endl;
		list<string>l;
		char buf[10];
		clock_t timestate = clock();
		srand((int)time(NULL));
		for (long i = 0; i < value; i++){
			try{
				snprintf(buf,10,"%d",rand());
				l.push_back(string(buf));
			}
			catch (exception & p){
				cout << "i = " << i <<" "<< p.what() << endl;
				abort();
			}
		}

		cout << "milli-seconds of rand list l : " << (clock() - timestate) << endl;
		cout << "list l.size() = " << l.size() << endl;
		cout << "list l.max_size() = " << l.max_size() << endl;
		cout << "list l.front() = " << l.front() << endl;
		cout << "list l.back() = " << l.back() << endl;
	//上述程序已经生成了 value 个元素的链表
		string target = exp1::get_a_target_string();
		{
			timestate = clock();
			auto PItem = ::find(l.begin(), l.end(), target);
			cout << "::find the list target  milli-seconds : " << (clock() - timestate) << endl;
			if (PItem != l.end())
				cout << "found the list target : " << *PItem << endl;
			else
				cout << "not found the list " << endl;
		}

		timestate = clock();
		l.sort();
		cout << "the list of the  l.sort(),milli-seconds : " << (clock() - timestate)<<endl;
	}
}

int main()
{
	long value =500000;
	test_list::test_list(value);
	system("pause");
	return 0;
}

  •   forward_list容器的测试程序

#include<iostream>
#include<ctime>
#include<cstdlib>
#include<cstdio>
#include<forward_list>
using namespace std;

namespace test_forward_list{
	void test_forward_list(long & value)
	{
		cout << "\n test_forward_list()........\n" << endl;
		char buf[10];
		forward_list<string>f;
		clock_t timestate = clock();
		srand((int)time(NULL));
		for (long i = 0; i < value; i++){
			try{
				snprintf(buf, 10, "%d", rand());
				f.push_front(string(buf));
			}
			catch (exception & p){
				cout << "i = " << i << "  " << p.what() << endl;
				abort();
			}
		}
		
		cout << "milli-seconds of rand froward_list  f : " << (clock() - timestate) << endl;
		cout << "froward_list f..max_size() = " << f.max_size() << endl;
		cout << "froward_list f.front() = " << f.front() << endl;
		
		string target = exp1::get_a_target_string();
		{
			timestate = clock();
			auto PItem = ::find(f.begin(), f.end(), target);
			cout << "::find the forward_list target  milli-seconds : " << (clock() - timestate) << endl;
			if (PItem != f.end())
				cout << "found the forward_list target : " << *PItem << endl;
			else
				cout << "not found the forward_list " << endl;
		}

		timestate = clock();
		f.sort();
		cout << "the forward_list of the  f.sort(),milli-seconds : " << (clock() - timestate) << endl;
	}
}


int main()
{
	long value =500000;
	test_forward_list::test_forward_list(value);
	system("pause");
	return 0;
}

  •   deque容器的测试程序

#include<iostream>
#include<ctime>
#include<cstdlib>
#include<cstdio>
#include<deque>
using namespace std;

namespace test_deque{
	void test_deque(long & value)
	{
		cout << "\n test_deque()........\n" << endl;
		char buf[10];
		deque<string>d;
		clock_t timestate = clock();
		srand((int)time(NULL));
		for (long i = 0; i < value; i++){
			try{
				snprintf(buf, 10, "%d", rand());
				d.push_front(string(buf));
			}
			catch (exception & p){
				cout << "i = " << i << "  " << p.what() << endl;
				abort();
			}
		}

		cout << "milli-seconds of rand test_deque  f : " << (clock() - timestate) << endl;
		cout << "test_deque d.max_size() = " << d.max_size() << endl;
		cout << "test_deque d.front() = " << d.front() << endl;

		string target = exp1::get_a_target_string();
		{
			timestate = clock();
			auto PItem = ::find(d.begin(), d.end(), target);
			cout << "::find the test_deque target  milli-seconds : " << (clock() - timestate) << endl;
			if (PItem != d.end())
				cout << "found the test_deque target : " << *PItem << endl;
			else
				cout << "not found the test_deque " << endl;
		}
	}
}

int main()
{
	long value =500000;
	test_deque::test_deque(value);
	system("pause");
	return 0;
}

  •   stack栈的测试程序
#include<iostream>
#include<ctime>
#include<cstdlib>
#include<cstdio>
#include<stack>
using namespace std;

namespace test_stack{
	void test_stack(long & value)
	{
		cout << "\n test_stack()........\n" << endl;
		char buf[10];
		stack<string>st;
		clock_t timestate = clock();
		srand((int)time(NULL));
		for (long i = 0; i < value; i++){
			try{
				snprintf(buf, 10, "%d", rand());
				st.push(string(buf));
			}
			catch (exception & p){
				cout << "i = " << i << "  " << p.what() << endl;
				abort();
			}
		}

		cout << "milli-seconds of rand test_stack  st : " << (clock() - timestate) << endl;
		cout << "test_stack st.size() = " << st.size() << endl;
		cout << "test_stack st.top() = " << st.top()<<endl;
			st.pop();
		cout << "test_stack st.size() = " << st.size() << endl;
		cout << "test_stack st.top() = " << st.top() << endl;
/**********************************************************************************************/
		//给 stack (栈)中元素排序需要先定义一个暂时缓冲栈
		//以下为给栈 st 中的元素进行排序
		timestate = clock();
		stack<string>Help;
		while (!st.empty())
		{
			string Curvalue = st.top();//先将栈顶元素给一个中间字符串元素
			st.pop();
			while (!Help.empty() && Curvalue > Help.top())//在另外一个栈中寻找插入位置
			{
				st.push(Help.top());
				Help.pop();
			}
			Help.push(Curvalue);
		}
		while (!Help.empty())
		{
			string val = Help.top();
			st.push(val);
			//cout << Help.top() << "  ";
			Help.pop();
		}
		cout << endl;
		cout << "sort  of  stack,milli-seconds : " << (clock() - timestate) << endl;
/**********************************************************************************************/
	}
}


int main()
{
	long value =500000;
	test_stack::test_stack(value);
	system("pause");
	return 0;
}

  •   queue堆的测试程序

#include<iostream>
#include<ctime>
#include<cstdlib>
#include<cstdio>
#include<queue>
using namespace std;

namespace test_queue{
	void test_queue(long & value)
	{
		cout << "\n test_queue()........\n" << endl;
		char buf[10];
		queue<string>que;
		clock_t timestate = clock();
		srand((int)time(NULL));
		for (long i = 0; i < value; i++){
			try{
				snprintf(buf, 10, "%d", rand());
				que.push(string(buf));
			}
			catch (exception & p){
				cout << "i = " << i << "  " << p.what() << endl;
				abort();
			}
		}

		cout << "milli-seconds of the test_queue  que : " << (clock() - timestate) << endl;
		cout << "test_queue st.size() = " << que.size() << endl;
		cout << "test_queue st.front() = " << que.front() << endl;
		cout << "test_queue st.back() = " << que.back() << endl;
		que.pop();
		cout << "test_queue st.size() = " << que.size() << endl;
		cout << "test_queue st.front() = " << que.front() << endl;
		cout << "test_queue st.back() = " << que.back() << endl;
	}
}

int main()
{
	long value =500000;
	test_queue::test_queue(value);
	system("pause");
	return 0;
}

  上述的几个测试程序都是sequence containers类型,那么对于Associatice containers类型的容器又会如何呢?下面给出了Multiset和MultiMap类型的容器的测试程序:


  •   multiset容器的测试程序

/***********             ***********/

//测试associative container 的效率

/***********             ***********/
#include<iostream>
#include<ctime>
#include<set>
#include<cstdlib>
#include<cstdio>
using namespace std;

namespace test_multiset{
	void test_multiset(long & value)
	{
		cout << "\n test_multiset().........\n";
		char buf[10];
		multiset<string>mu;
		clock_t timestate = clock();
		srand((int)time(NULL));
		for (long i = 0; i < value; i++){
			try{
				snprintf(buf, 10, "%d", rand());
				mu.insert(string(buf));//插入数据
			}
			catch (exception & p){
				cout << "i = " << i << "  " << p.what() << endl;
				abort();
			}
		}

		cout << "milli-seconds of the test_multiset mu : " << (clock() - timestate) << endl;
		cout << "test_multiset  mu.size() = " << mu.size() << endl;
		cout << "test_multiset mu.max_size()" << mu.max_size() << endl;
		
		string target = exp1::get_a_target_string();
		{
			timestate = clock();
			auto PItem = ::find(mu.begin(), mu.end(), target);
			cout << "tets_multiset ::find  milli-seconds : " << (clock() - timestate) << endl;
			if (PItem != mu.end())
				cout << "test_multiset ::find the target : " << *PItem << endl;
			else
				cout << "test_multiset not found the target ! " << endl;
		}
		{
			timestate = clock();
			auto PItem = mu.find(target);//multiset 自身的find算法
			cout << "tets_multiset find  milli-seconds : " << (clock() - timestate) << endl;
			if (PItem != mu.end())
				cout << "test_multiset find the target : " << *PItem << endl;
			else
				cout << "test_multiset not found the target ! " << endl;
		}
	}
}

int main()
{
	long value =500000;
	test_multiset::test_multiset(value);
	system("pause");
	return 0;
}

  •   multiMap容器的测试程序
#include<iostream>
#include<map>
#include<ctime>
#include<cstdio>
#include <stdexcept>
#include <string>
#include <cstdlib> //abort()
#include <cstdio>  //snprintf()
#include<utility>
using namespace std;

namespace test_multimap{
	void test_multimap(long & value){
		cout << "\n test_multimap()........\n" << endl;
		char buf[10];
		multimap<long, string>mu_map;//定义一个multimap的对象
		clock_t timestate = clock();
		srand((int)time(NULL));
		for (long i = 0; i < value; i++){
			try{
				snprintf(buf, 10, "%d", rand());
				//multimap 不可使用 [] 做 insertion 
				mu_map.insert(pair<long, string>(i, buf));//************插入时为键+值
			}
			catch (exception & p){
				cout << "i = " << i << "  " << p.what() << endl;
				abort();
			}
		}

		cout << "milli-seconds of the test_multimap mu_map : " << (clock() - timestate) << endl;
		cout << "test_multimap mu_map.max_size() = " << mu_map.max_size() << endl;
		cout << "test_multimap mu_map.size() = " << mu_map.size() << endl;

		long target = exp1::get_a_target_long();
		{
		timestate = clock();
		auto PItem = ::find(mu_map.begin(), mu_map.end(), target);
		cout << "milli-secpnds ::find test_multimap : " << (clock() - timestate) << endl;
			if (PItem != mu_map.end())
				cout << "test_multimap ::  found," << (*PItem).second << endl;
			else
				cout << "test_multimap not found !" << endl;
		}
		{
			timestate = clock();
			auto PItem = mu_map.find(target);
			cout << "milli-secpnds find test_multimap : " << (clock() - timestate) << endl;
			if (PItem != mu_map.end())
				cout << "test_multimap ::  found," << (*PItem).second << endl;
			else
				cout << "test_multimap not found !" << endl;
		}
	}
}


int main()
{
	long value =500000;
	test_multimap::test_multimap(value);
	system("pause");
	return 0;
}

  •   补充:下面主要补充一下try\catch\throw的 使用

  1.“try”总是与“catch”一同出现,伴随一个try语句,至少应该有一个catch()语句,try随后的block是可能抛出异常的地方。


  2.“catch”带有一个参数,参数类型以及参数名字都由程序指定,名字可以忽略。如果在catch随后的block中并不打算引用这个异常对象的。参数类型可以是build-intype,例如int、long、char等,也可以是一个对象,一个对象指针或者引用,如果希望捕获任意类型的异常,可以使用“…”作为catch的参数。


  3.“catch”不一定要全部捕获tryblock中抛出的异常,剩下没有捕获的可以交给上一级函数处理。


  4.“throw”throw类型后面带一个类型的实例,它和catch的关系就像是函数的调用,catch指定形参,throw给出实参。编译器按照catch给定的顺序以及catch指定的参数类型,确定一个异常由哪一个catch来处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值