一、迭代器的几个细节问题
1、const与非const
(1)begin和end返回可读可写的迭代器,而cbegin和cend返回const的只读迭代器 (2)代码验证迭代器的读写权限
#include <iostream>
#include <array>
using namespace std;
int main(int argc, char *argv[])
{
array<int, 3> a = {1, 2, 3};
#if 0
array<int, 3>::iterator iter;
array<int, 3>::const_iterator citer;
for(iter = a.begin(); iter != a.end(); iter++)
{
cout << "iter = " << iter << endl;
cout << "*iter = " << *iter << endl;
*iter = 11;
}
for(citer = a.cbegin(); citer != a.cend(); citer++)
{
cout << "citer = " << citer << endl;
cout << "*citer = " << *citer << endl;
}
#else
for(auto iter = a.begin(); iter != a.end(); iter++)
{
cout << "iter = " << iter << endl;
cout << "*iter = " << *iter << endl;
*iter = 11;
}
for(auto citer = a.cbegin(); citer != a.cend(); citer++)
{
cout << "citer = " << citer << endl;
cout << "*citer = " << *citer << endl;
}
#endif
return 0;
}
2、begin和end的半开半闭区间
(1)begin返回第0个元素的迭代器(类似于C数组的首元素首地址指针)
(2)end指向的不是末尾元素的迭代器,而是末尾元素的(实际不存在的)下一个元素的迭代器
(3)前闭后开区间,经常记做[begin end),这样设计是为了写循环遍历时方便
3、正向和逆向迭代器
(1)rbegin和rend返回逆向迭代器
(2)逆向迭代器的begin是最末尾元素,而end是第0个元素去(实际不存在的)前面1个元素的迭代器
(3)逆向迭代器++是向前移动,而–是向后移动 所谓逆向是指数据存储的顺序变了,例如原来地址由低到高存着1、2、3,逆向后,地址由低到高存着3、2、1。begin开始的哪里一直是闭区间,end一直是开区间。
#include <iostream>
#include <array>
#include <cstdio>
using namespace std;
int main(int argc, char *argv[])
{
array<int, 3> a = {1, 2, 3};
for(auto iter = a.begin(); iter != a.end(); iter++)
{
cout << "iter = " << iter << endl;
cout << "*iter = " << *iter << endl;
}
for(auto citer = a.crbegin(); citer != a.crend(); citer++)
{
cout << "*citer = " << *citer << endl;
}
return 0;
}
4、迭代器越界会怎么样
(1)和数组越界类似,编译时不审查,运行时会崩溃
(2)不管是正向还是逆向迭代器,++不到end,–不到begin,就不会越界
二、STL的不同类型迭代器
1、C++17前共有5种迭代器
(1)InputIterator,输入迭代器。只能从容器内读出而不能向容器内写入,只能单次读出(读出过一次后不保证再次操作仍然可以,想想流输入输出),只能++走不能–走(就是单向的),不能保证第二次遍历容器时,顺序不变。输入迭代器适用于单通只读型算法。
(2)OutputIterator,输出迭代器。用于将信息传输给容器(修改容器中元素的值),但是不能读取。(显示器就是只能写不能读的设备,可用输出容器来表示它)只能++走不能–走(就是单向的),输出迭代器适用于单通只写型算法。
(3)ForwardIterator,前向迭代器。只能++走不能–走(就是单向的)
(4)BidirectionalIterator,双向迭代器。既能++也可以–,双向移动。
(5)RandomAccessIterator,随机访问迭代器。能双向移动,并且可以单次跨越多个元素移动。
2、C++17新增1种迭代器
(1)contiguousIterator,连续迭代器。所指向的逻辑相邻元素也在内存中物理上相邻。
3、STL的6种迭代器总结
(1)每种迭代器更应该被看作是具有某些预定义特征(或者满足某接口要求)的一个迭代器的实现。
(2)这些迭代器彼此之间有功能重叠,譬如随机访问迭代器可由双向迭代器扩展而来,详见文档:https://zh.cppreference.com/w/cpp/iterator
(3)为何定义多种迭代器? ??是为了适配容器特性和泛型算法,后面会看到array的迭代器既是一个双向迭代器,也是一个随机访问迭代器。
4、C++20的新迭代器
(1)C++20中重新实现了基于concept的新的迭代器体系
(2)原有的模板都被加了前缀Legecy,但很长时间仍然可用,甚至还是主流
(3)基于concept的新迭代器主要在类型约束和模板特化方面做了优化
(4)C++20目前还刚开始,可以先不管,先学好原有的,后面再扩展去学C++20新特性
三、序列容器之Vector
参考学习文档:https://zh.cppreference.com/w/cpp/container/vector
1、Vector的特征
(1)Vector和Array相同点是:都是数组、都是contiguousIterator、容器内元素种类都相同
(2)Vector和Array不同点是:Array是固定数组;Vector是动态数组,可以按需扩展数组大小
(3)vector 的存储是自动管理的,按需扩张收缩。
(4)vector 通常占用多于静态数组的空间,因为要分配更多内存以管理将来的增长
(5)vector 所用的方式不在每次插入元素时,而只在额外内存耗尽时重分配。
2、构造拷贝和赋值
容量相关的成员函数:
size:返回容纳的元素数,当前的元素数
max_size:返回可容纳的最大元素数,即可扩充的最大元素数,因为vector是动态数组
capacity:返回当前存储空间能够容纳的元素数
C++11 noexcept: https://blog.csdn.net/luoshabugui/article/details/86255100
C++ explicit: https://blog.csdn.net/guoyunfei123/article/details/89003369
(1)多种参数的构造、拷贝构造
https://zh.cppreference.com/w/cpp/container/vector/vector
其具有多种拷贝构造函数,若遇到没有见过的初始化方式,可以查看属于哪一种构造函数。
拷贝构造函数,要考虑到深拷贝和浅拷贝
(3)assign
3、容器元素的遍历
(1)最本质的笨办法:获取容器迭代器,再写for/while循环来遍历 (2)C++11:Range-Based for循环,案例参考assign函数页面
#include <vector>
#include <iostream>
int main()
{
std::vector<char> characters;
characters.assign(5, 'a');
for (char c : characters) {
std::cout << c << ' ';
}
characters.assign({'\n', 'C', '+', '+', '1', '1', '\n'});
for (char c : characters) {
std::cout << c;
}
}
输出:
a a a a a
C++11
??本容器的其他一些操作方法与array相同,可查看该类相关的文档,了解使用: https://zh.cppreference.com/w/cpp/container/vector
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> vec;
int i;
cout << "vector size = " << vec.size() << endl;
for(i = 0; i < 5; i++){
vec.push_back(i);
}
cout << "extended vector size = " << vec.size() << endl;
for(i = 0; i < 5; i++){
cout << "value of vec [" << i << "] = " << vec[i] << endl;
}
vector<int>::iterator v = vec.begin();
while( v != vec.end()) {
cout << "value of v = " << *v << endl;
v++;
}
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
vector size = 0
extended vector size = 5
value of vec [0] = 0
value of vec [1] = 1
value of vec [2] = 2
value of vec [3] = 3
value of vec [4] = 4
value of v = 0
value of v = 1
value of v = 2
value of v = 3
value of v = 4
关于上面实例中所使用的各种函数,有几点要注意:
push_back( ) 成员函数在向量的末尾插入值,如果有必要会扩展向量的大小。
size( ) 函数显示向量的大小。
begin( ) 函数返回一个指向向量开头的迭代器。
end( ) 函数返回一个指向向量末尾的迭代器。
四、序列容器之list
参考学习:https://zh.cppreference.com/w/cpp/container/list (1) list 通常实现为双向链表。 insert、emplace的效果是相同的,但实现过程是有差别的,从二者的参数可以看出。 ??以上成员函数的具体使用方法与例程,请打开上边提供的网址,该网址大家可以保留下。便于自己在进行C++开发时,及时查询相关的知识。
#include <iostream>
#include <list>
using namespace std;
int main(int argc, char **argv)
{
list<int> l = {1, 2, 3, 4, 5, 6};
cout << "l.size = " << l.size() << endl;
cout << "l.max_size = " << l.max_size() << endl;
cout << "is or not empty :" << l.empty() << endl;
for(auto c : l)
{
cout << c << ' ';
}
cout << endl;
l.assign(6, 0);
l.push_front(1);
l.push_back(9);
l.insert(l.begin(), 0);
l.sort();
for(auto iter = l.begin(); iter != l.end(); iter++)
{
cout << *iter << ' ';
}
cout << endl;
return 0;
}
五、序列容器之deque
??deque容器为一个给定类型的元素进行线性处理,像向量一样,它能够快速地随机访问任一个元素,并且能够高效地插入和删除容器的尾部元素。但它又与vector不同,deque支持高效插入和删除容器的头部元素,因此也叫做双端队列。
参考学习: https://blog.csdn.net/u010710458/article/details/79540505 https://zh.cppreference.com/w/cpp/container/deque
#include <iostream>
#include <deque>
int main()
{
std::deque<int> d = {7, 5, 16, 8};
d.push_front(13);
d.push_back(25);
for(int n : d) {
std::cout << n << '\n';
}
}
输出:
13
7
5
16
8
25
注:本文章参考了《朱老师物联网大讲堂》课程笔记,并结合了自己的实际开发经历以及网上他人的技术文章,综合整理得到。如有侵权,联系删除!水平有限,欢迎各位在评论区交流。
|