一、List认识介绍
什么是list
template < class T, class Alloc = allocator<T> > class list;
这是文档给出的定义
所以什么是List?
List是序列容器,允许在序列中的任意位置执行固定时间的插入和擦除操作,并在两个方向上进行迭代。
它们与forward_list非常相似:主要区别在于forward_list对象是单链表,因此它们只能向前迭代,以换取更小和更高效
与其他基本标准序列容器(array、vector和deque)相比,链表在容器内任何位置插入、提取和移动元素(已获得迭代器)以及在大量使用这些元素的算法(如排序算法)中通常表现得更好
与其他序列容器相比,list和 forward_list的主要缺点是,它们无法通过位置直接访问元素;例如,要访问列表中的第六个元素,必须从已知位置(如开始或结束)迭代到该位置,这需要在这些位置之间的距离上花费线性时间。它们还消耗一些额外的内存来保持与每个元素相关联的链接信息(这可能是小型元素的大型列表的一个重要因素)。
List文档介绍
二、List结构介绍
List容器实现为双链接链表;双链接链表可以将它们包含的每个元素存储在不同的、不相关的存储位置。在内部,顺序是通过与每个元素的关联来保持的,这些元素是指向它前面的元素的链接和指向它后面的元素的链接。
三、List的使用
3.1 list的构造
list() 构造空的list
list (size_type n, const value_type& val = value_type()) 构造的list中包含n个值为val的元素
list (const list& x) 拷贝构造函数
list (InputIterator first, InputIterator last) 用[first, last)区间中的元素构造list
list<int> l;
list<int> li(7,0);
list<int> v(li);
list<int>v1(li.begin(), li.end());
for (auto o : v) {
cout << 0 << " ";
}
cout << endl;
for (auto o : li) {
cout << 0 << " ";
}
cout << endl;
l调用无参构造new一个空对象,
li调用有参构造new一个初始化n个var参数的有参对象,
v通过拷贝构造函数将里中的数据拷贝到自己内存中,
v1用li的[begin(), end())左闭右开的区间构初始化自己。
3.2 list的迭代器
begin +end
返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器
rbegin +rend
返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的reverse_iterator,即begin位置
因为list的内存空间不是连续的,但是每个节点都保存了上一个节点和下一个节点的地址所以list在使用迭代器时first指向第一个节点的位置,因为保存了上一个节点和下一个节点的地址所以++和–都是取这个节点所指向的下一个或上一个节点的地址就行了
- begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
- rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动
3.4 list capacity
empty 检测list是否为空,是返回true,否则返回false
size 返回list中有效节点的个数
list<int> l;
list<int> li(7);
std::cout << l.empty() << endl;
std::cout << li.empty() << endl;
std::cout << l.size() << endl;
std::cout << li.size() << endl;
因为l是调用无参构造new一个空对象所以empty为1,size()为1
li用7个var是初始化的有参构造所以empty为0,size()为7
3.5 list element access
front 返回list的第一个节点中值的引用
back 返回list的最后一个节点中值的引用
list<int> l;
list<int> li(7);
std::cout << li.front() << endl;
std::cout << li.back() << endl;
front就是双链表第一个节点的地址,
back就是双链表最后一个节点的地址
3.6 list modifiers
push_front 在list首元素前插入值为val的元素
pop_front 删除list中第一个元素
push_back在list尾部插入值为val的元素
pop_back 删除list中最后一个元素
insert 在list position 位置中插入值为val的元素
erase 删除list position位置的元素
swap 交换两个list中的元素
clear清空list中的有效元素
list<int> l(4,2);
list<int> li(6,1);
for (auto o : li) {
std::cout << o << " ";
}std::cout << endl;
li.push_front(5);
for (auto o : li) {
std::cout << o << " ";
}std::cout << endl;
li.pop_front();
for (auto o : li) {
std::cout << o << " ";
}std::cout << endl;
li.push_back(2);
for (auto o : li) {
std::cout << o << " ";
}std::cout << endl;
li.pop_back();
for (auto o : li) {
std::cout << o << " ";
}std::cout << endl;
li.insert(li.begin(), 3);
for (auto o : li) {
std::cout << o << " ";
}std::cout << endl;
li.erase(li.begin());
for (auto o : li) {
std::cout << o << " ";
}std::cout << endl;
l.swap(li);
for (auto o : l) {
std::cout << o << " ";
}std::cout << endl;
for (auto o : li) {
std::cout << o << " ";
}std::cout << endl;
li.clear();
for (auto o : li) {
std::cout << o << " ";
}std::cout << endl;
初始化li为6个1,l为4个2push_front 在list首元素前插入值为val的元素 pop_front 删除list中第一个元素 push_back在list尾部插入值为val的元素 pop_back 删除list中最后一个元素insert 在list begin() 位置中插入值为val的元素 erase 删除list position位置的元素 swap 交换两个list中的元素clear清空list中的有效元素
3.7 list的迭代器失效
list<int> l(4,2);
list<int> li(6,1);
li.push_back(10);
li.push_back(30);
li.push_back(40);
li.push_back(50);
auto it = find(li.begin(), li.end(), 10);
li.erase(it);
cout << *it << endl;
在输出*it为什么会报错?
因为在把pos删除后这个节点会释放pos会变成野指针,所以在输出pos会报错为无效参数。
那么在insert()插入数据时会迭代器会失效吗?
答案是不会的因为在list不像vector内存空间是连续的在insert()时如果空间不够时会开辟新空间,释放老空间,因为pos指向
老空间所以pos就会失效。而list的能存空间不是连续的在nsert()时不会开辟新空间,只会为新节点开辟新空间让他指向pos的前一个节点和pos节点,所以没有空间的释放就没有迭代器的失效
vector底层是物理连续数组。可能扩容。导致野指针问题。不扩容,挪动数据,也导致pos 意义变了。
list是一个个独立节点,在pos前边新增一个节点pos还是指向这个节点。不会变成一个,意义没变。