C++ 列表迭代器与向量迭代器

C++ 列表迭代器与向量迭代器,c++,stl,iterator,stdvector,stdlist,C++,Stl,Iterator,Stdvector,Stdlist,我有两个关于迭代器的问题 我认为,一旦您为STL容器(如向量或列表)定义了迭代器,如果您向容器添加元素,那么这些迭代器将无法访问它们。但是下面的代码定义了一个包含五个元素的列表,然后在每个循环迭代中添加另一个元素,并生成一个无限循环: #include <iostream> #include <list> using namespace std; int main() { list<int> ls; for(int i = 0; i &l

我有两个关于迭代器的问题

  • 我认为,一旦您为STL容器(如向量或列表)定义了迭代器,如果您向容器添加元素,那么这些迭代器将无法访问它们。但是下面的代码定义了一个包含五个元素的列表,然后在每个循环迭代中添加另一个元素,并生成一个无限循环:

    #include <iostream>
    #include <list>
    
    using namespace std;
    
    int main()
    {
        list<int> ls;
    
        for(int i = 0; i < 5; i++)
        {
            ls.push_back(i);
        }
    
        int idx = 0;
    
        for(list<int>::iterator iter = ls.begin(); iter != ls.end(); iter++)
        {
            cout << "idx: " << idx << ", *iter: " << *iter << endl;
            ls.push_back(7);
            idx++;
        }
    }
    
    #包括
    #包括
    使用名称空间std;
    int main()
    {
    列表ls;
    对于(int i=0;i<5;i++)
    {
    ls.推回(i);
    }
    int-idx=0;
    对于(列表::迭代器iter=ls.begin();iter!=ls.end();iter++)
    {
    
    第一个问题的答案包含在第二个问题中


    至于第二个问题,则是实现定义了向量如何分配内存。没有必要在每次内存耗尽时将其大小加倍。

    不同容器的迭代器具有不同的属性

    列表循环:当你推到一个
    列表上时
    之前的所有迭代器仍然有效。显然,如果每次迭代器转发一个迭代器时,你也添加了一个新元素,那么你永远不会到达终点

    向量循环:对于向量,如果
    push_-back
    导致新大小超过旧容量,则迭代器无效。一旦发生这种情况,使用
    iter
    是未定义的行为(您可能会崩溃)

    我认为当向量容器必须调整大小时,它会这样做 以2的幂运算,并位于新的内存区域


    < C++标准库的一些实现,当大小超过旧容量时,向量的容量增加一倍,而其他的增长率不同。

    < P>不同的容器通常对迭代器的有效性和元素的指针/引用有不同的保证:

  • 对于
    std::list
    元素的迭代器和指针/引用将保持有效,直到相应的节点被删除或
    std::list
    存在
  • 对于
    std::vector
    而言,有效性更为复杂:
  • 迭代器和指针/引用的有效性是相同的(下面我只使用迭代器)
  • std::vector
    需要调整其内部缓冲区的大小时,即插入超过容量时,所有迭代器都将无效。超出容量时以及分配的内存量取决于实现(唯一的要求是容量呈指数增长,2是一个合理的选择,但还有很多其他因素)
  • 当插入到
    std::vector中时,除非需要重新分配,否则插入点之前的所有迭代器都将保持有效
  • std::vector
    中擦除时,所有经过擦除点的迭代器均无效

  • 然而,其他容器具有不同的有效性约束(例如,
    std::deque
    保持迭代器无效,但可以保持指针/引用有效).

    不要假设。stdlib类的文档清楚地描述了容器上某些类型的变异何时以及哪些迭代器无效。以下是关于迭代器何时无效的一个很好的答案:请注意,如果只看指针值,无效迭代器可能与有效迭代器无法区分当
    vector
    调整大小时,它的新存储可能会从旧存储相同的地址开始。即使保证
    vector
    增加了它的
    容量()
    ,您不能保证它位于不同的地址。只有
    vector
    的规则告诉您迭代器何时被视为有效或无效。标准不强制要求
    vector
    的容量增加速率。实现可以以1.5倍、2倍、3倍或任何它想要的速率进行,只要保持不变他分摊了
    push_back()
    O(1)的复杂性(并保留了
    vector
    的其他方法的复杂性要求)。@Dave你所说的“实现”是什么意思我使用Visual C++ 2010 Express,我假设这与我使用Emacs不同。@戴维Emacs只是一个编辑器。我在讨论标准库的实现。VS2010附带了一个。Emacs不。微软有选择地增长向量的容量,但是他们想要的。它没有在标准中指定如何生长它。t实现可以以不同的速度增长。@David:“实现”是C++编译器及其标准C++库。VisualStudioC++是C++编译器损坏的。Emacs没有。有多种不同的编译器,例如,是流行的开源编译器。@ dieMuxu HL你会比我知道得更好,但是你所看到的“实现定义”和“未指定”之间的语义区别。很模糊——你能详细说明吗?它们对我来说意味着同样的事情。”戴夫:“实现定义”意味着实现文档需要明确地记录它所选择的内容,这些选择成为契约的一部分。只是C++标准没有做出任何选择(参见,例如)。这意味着实现需要做一些明智的事情,可能会有不同的选择产生不同的结果,但这些选择没有记录,也无法保证。DS9k实现可以选择每次做出不同的选择。
    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    int main()
    {
        vector<int> vec;
    
        for(int i = 0; i < 5; i++)
        {
            vec.push_back(i);
        }
    
        int idx = 0;
    
        for(vector<int>::iterator iter = vec.begin(); iter != vec.end(); iter++)
        {
            cout << "idx: " << idx << ", *iter: " << *iter << endl;
            vec.push_back(7);
            idx++;
        }
    }
    
    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    int main()
    {
        vector<int> vec;
    
        for(int i = 0; i < 4; i++)
        {
            vec.push_back(i);
            cout << "address of vec: " << &vec << ", capacity: " << vec.capacity() << endl;
        }
    
    
    
        for(int i = 0; i < 20; i++)
        {
            vec.push_back(i);
            cout << "address of vec: " << &vec << ", capacity: " << vec.capacity() << endl;
        }
    }