在C++实际开发过程中,经常需要实现数据的添加、修改、插入、读取等功能,若程序员自己编写一个数据结构来实现,不仅会增加代码的工作量,还会出现许多需要解决的问题。而容器是一种封装了数据结构的类,可实现各种功能,帮助程序员更好地开发。
常用的标准容器可分为两大类,序列容器和关联容器。本文先浅谈序列容器的分类、特点及使用情况,希望帮助开发者选择最合适的容器。
序列容器可分为三种,vector向量容器、list列表容器、deque双端队列容器,如下:
vector向量容器:可看作一个动态数组,连续存储结构;
list链表容器:链表结构,在内存中非连续存储;
deque双端队列容器:队列结构,在内存中不一定连续存储;
1、相当于一个动态数组,可实现容器长度的变化,在内存上占有一段连续的地址空间;
2、擅长随机访问以及在容器尾部添加或删除元素,效率较高;
3、在容器头部及中间位置插入或删除元素,需要移动该位置以后的所有元素,效率较低;
vector容器的常见用法,用一段程序来展示:
#include
using namespace std;
#include
int main()
{
vector<int> v1{
11, 12, 13, 14, 15 }; //初始化容器
//1、迭代器法访问容器的所有元素
//v1.begin()、v1.end()分别为容器第一个元素和最后一个元素的迭代器
for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
{
cout << *it << " "; //it是循环迭代器,实际上是一个指针,指向容器中的某一个元素的地址,*it就可以得到元素的值
}
cout << endl;
v1.pop_back(); //移除容器中的最后一个元素
v1.push_back(20); //容器尾部添加元素
//2、for循环法访问容器的所有元素
for (int i = 0; i < v1.size(); i++) //v1.size()为容器中的元素个数
{
cout << v1.at(i) << " "; //v1.at()返回容器中第i个元素的值
}
cout << endl;
system("pause");
return 0;
}
程序用itertator迭代器法和for循环法两种方法来遍历容器,从而引出了begin()、end()、pop_back()、push-back()、at()、size()等容器命令的常见用法,希望读者特别注意理解迭代器法遍历容器的原理。insert()等命令由于效率较低,不建议使用。
1、容器中各个元素的前后顺序靠指针联系在一起,在内存中不一定是连续的地址空间;
2、擅长随机插入或删除元素,只改变前后两个元素的联系,效率高;
3、不擅于随机访问元素,遍历容器需要访问大量指针,效率极低;
list容器的常见用法,用一段程序来展示:
#include
using namespace std;
#include
int main()
{
list<int> list1{
11, 12, 13, 14, 15 }; //初始化容器
//insert常用用法
list1.insert(list1.begin(), 20); //在容器首部添加元素20
list1.insert(list1.end(), 2, 10); //在容器尾部添加2个10
list1.insert(++list1.begin(), 30); //在第2个元素前插入元素30
//迭代器法遍历容器,打印容器所有元素
for (list<int>::iterator it = list1.begin(); it != list1.end(); it++)
{
cout << *it << " ";
}
cout << endl;
//利用插入和删除命令来实现第四个元素的替换
list<int>::iterator it = list1.begin();
for ( int i = 0; it != list1.end() && i < 3; i++)
{
++it; //迭代器指针指向下一个位置
}
list1.insert(it, 66); //插入新的元素后,迭代器自动更新指向下一个元素
list1.erase(it); //删除第五个元素,完成替换
//迭代器法遍历容器,打印容器所有元素
for (list<int>::iterator it = list1.begin(); it != list1.end(); it++)
{
cout << *it << " ";
}
cout << endl;
system("pause");
return 0;
}
运行结果:
该程序主要帮助读者熟悉list容器中insert()、erase()命令的用法,这也是学习list容器需要掌握的最基本、最重要的命令之一,其他常用命令和vector容器类似,在此不再说明。不难看出,list容器的使用相比vector容器更复杂,其原理更是如此,读者可以去阅读《数据结构与算法》中链表一章就会有更深入的理解。
1、将多个连续内存空间通过指针数组连接在一起,不能保证所有元素在同一段连续的地址空间中;
2、容器的地址空间是动态变化的,可以向两端进行伸缩,内部实现比较复杂;
3、擅长在容器首部、尾部插入或删除元素,效率高;
deque容器的常见用法,用一段程序来展示:
#include
using namespace std;
#include
int main()
{
deque<int> d1{
11, 12, 13, 14, 15 }; //初始化容器
d1.push_back(20); //在容器尾部添加元素20,向后扩展4个字节的内存空间
d1.push_front(5); //在容器首部添加元素5,向前增加4个字节的内存空间
//迭代器法遍历容器,打印容器所有元素
for (deque<int>::iterator it = d1.begin(); it != d1.end(); it++)
{
cout << *it << " ";
}
cout << endl;
d1.pop_back(); //移除容器尾部元素
d1.pop_front(); //移除容器首部元素
//迭代器法遍历容器,打印容器所有元素
for (deque<int>::iterator it = d1.begin(); it != d1.end(); it++)
{
cout << *it << " ";
}
cout << endl;
system("pause");
return 0;
}
运行结果:
以上程序仅涉及向容器首部、尾部添加及删除元素的功能,这也是deque相比另两个容器最有优势的功能。
通过对vector、list、deque三种容器的分析可知,每一种容器都有自己独特的优点以及缺陷,在开发过程中,读者可根据以下标准来选择最合适的容器:
当需要随机访问元素,只对序列尾部大量添加或删除元素时,应首选vector容器
当需要对序列中间大量插入或删除元素时,应首选list容器
当需要向序列首部、尾部大量添加或删除元素时,应首选deque容器
也许有人可能会问,只要满足了开发需要,选用哪种容器有这么重要嘛?
我的回答是yes,当我们写小程序时,可能会觉得无论选用哪种容器对运行速度的影响都很小,但是一旦进行大型项目开发时,选用不同容器的对内存的开销以及运行速度的差距可能会呈指数级别增大,这时候就非常考验程序员的基本功底了,因此平时的编程中一定要养成良好的习惯。