C++ STL——为什么deque.push_back()会使所有容器内的迭代器失效?

目录

引言

STL是如何实现Deque的

Deque.push_back()被执行时发生了什么?


引言

某天研究了下关于迭代器失效的问题,这个链接很清楚地说明了迭代器失效的场合,但是它没有解释原理。

在参考了视频后,我对Deque的架构有了基本理解,其中我最好奇的是:为什么Deque执行push_back指令后所有迭代器会失效?

必须结合Debug工具从源码的角度去分析一波。

工程属性:

Windows_SDK:10.0

平台工具集:Visual Studio 2019(v142)

C++语言标准:std:c++14

STL是如何实现Deque的

除了上述提及的视频之外,这篇博文也简单介绍了Deque的基本原理。

我个人理解是:分段的若干数组(线性存储空间),在视频中也被称作为node。数组的地址则有另一个线性存储空间存储。头位置和尾部位置的数组没有填满,以便push_front/push_back指令操作。

有人会问为什么Deque的底层架构是数组而不是链表/双向链表。我认为使用数组作为底层架构可以方便迭代器以常量时间随机访问Deque中的元素。所以我倾向于把Deque视作一个可以“便宜”地在头部插入/删除元素的vector

但是视频没有讲解push_back的原理,而博文中的解读是:

在push_front/push_back中,由于要满足以上预留一个节点的要求,若当前map所管理的节点个数不足以扩充时,map需要重分配。

但是当map所管理的节点个数充足,或者头部/尾部的线性存储空间没有被填满,也就是map不需要重分配时,执行push_back过后,先前的迭代器会失效吗?答案是会的。这与Deque.push_back()的底层实现有关。简单的编程可以例证这一说法。

Deque.push_back()被执行时发生了什么?

测试代码如下所示:

deque<int> myque;
for (int i = 0; i < 5; i++) myque.push_back(i);
auto iter = myque.begin();

iter++;
//断点1    
myque.push_back(5);
//断点2        
auto iter1 = myque.begin();
iter1++;
//断点3
cout << *iter1 << endl;

当执行到断点2时,迭代器变量iter的基类的成员值均变为空值。

这是因为push_back中调用了_Orphan_all()函数,它们的代码段分别是:

    void push_back(_Ty&& _Val) {
        _Orphan_all();
        _Emplace_back_internal(_STD move(_Val));
    }
inline void _Container_base12::_Orphan_all() noexcept {
#if _ITERATOR_DEBUG_LEVEL == 2
    if (_Myproxy) { // proxy allocated, drain it
        _Lockit _Lock(_LOCK_DEBUG);

        for (auto _Pnext = &_Myproxy->_Myfirstiter; *_Pnext; *_Pnext = (*_Pnext)->_Mynextiter) {
            (*_Pnext)->_Myproxy = nullptr;
        }

        _Myproxy->_Myfirstiter = nullptr;
    }
#endif // _ITERATOR_DEBUG_LEVEL == 2
}

Orphan_all()过程涉及到一个遍历的过程,其遍历了容器内创建的迭代器并将它们的成员_Proxy赋值为空指针。

同时,在Debug界面中,能够看见iterator类与_Myproxy的关系:

见图中选中部分:deque的iterator的实际成员包含_Myproxy

而从类的定义来看,Deque的迭代器继承自Iterator_base12,并在上述的Orphan_all()过程中其成员_Myproxy被置为nullptr:

struct _Iterator_base12 { // store links to container proxy, next iterator
    _Iterator_base12() noexcept : _Myproxy(nullptr), _Mynextiter(nullptr) {} // construct orphaned iterator
//......
}

template <class _Mydeque>
class _Deque_const_iterator : public _Iterator_base12

template <class _Mydeque>
class _Deque_iterator : public _Deque_const_iterator<_Mydeque> 

最终,我们可以做出结论,该版本中的Deque.push_back()过程中会无条件地使容器内的每个迭代器无效化。这与此前博文里提到的map数组被重分配导致迭代器失效是两个状况。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值