迭代器在添加到添加到列表向量的结构中时会停止指向某个值吗?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了迭代器在添加到添加到列表向量的结构中时会停止指向某个值吗?相关的知识,希望对你有一定的参考价值。

我正在做的项目的前提是使用迭代器而不是指针来制作跳过列表。我创建了一个节点列表向量。在节点结构中,它包含一个迭代器,它应该是保留位置的下面列表的迭代器。问题是当我创建一个新节点时,将其下面的迭代器设置为下面的迭代器列表,然后尝试通过引用它来访问它,它会出现故障。我认为这是因为迭代器没有被初始化并且它不能被解除引用,因为它似乎不是一个边界问题。


struct Node // in header file
{
  int value;
  list<Node>::iterator below;
   Node(int v, list<Node>::iterator b){
        value = v;
        below = b;
   }
   Node(){} 
   Node(int v){
        value = v;
   }


};


vector<list<Node>> skipList; //this is the skipList initialized in the header

//插入调用以将数字添加到跳过列表

void SkipLists::insert(int num){
    list<Node>::iterator loc;
    if(skipList.empty()){
        list<Node> nodes;
        nodes.push_back(Node(num));
        skipList.push_back(nodes);
    }else{
        loc = insertPlace(num, skipList[skipList.size()-1].begin(), skipList.size() -1);
        skipList[0].insert(loc, Node(num));

    }
    cout << "1.  " << *this << "\n\n\n";
    stack(num, loc);

    //this if statement also segfaults
    if(skipList.size() > 1){
        cout << (*(skipList[1].front().below)).value;
    }

}

//在insertPlace函数中,只有在进行递归调用时,它才会在while循环上进行segfaults。意味着添加到skiplist的先前值具有高度。解除引用它时会出现段错误。我通过将它移出while循环来测试它。

list<Node>::iterator SkipLists::insertPlace(int num, list<Node>::iterator it, int height){
    if(height == 0){
        while(it != skipList[0].end() && skipList[0].size() > 0 && num > (*it).value){            // problem: likely not returning a good (*it).below or never setting it properly. 
            it++;
        }
        return it;
    }
    while(it != skipList[height].end() && skipList[height].size() > 0 && num > (*it).value){
        cout << "he\n";
        it++;
        cout << "lo\n";
    }
    return insertPlace(num, (*it).below, --height);
}

stack用于根据概率在跳过列表中添加垂直元素。这是节点被赋予“下面”迭代器的地方。


void SkipLists::stack(int num, list<Node>::iterator loc){
    int flip = rand() % 2;
    int count = 1;
    list<Node>::iterator prev = loc;
    list<Node>::iterator it;
    while(flip == 1){
        count++;
        flip = rand() % 2;

        if(skipList.size() < count){
            list<Node> nodes;
            nodes.push_back(Node(num, prev));
            skipList.push_back(nodes);
            prev = skipList[skipList.size()-1].begin();
        }else{
            it = skipList[count-1].begin();
            while(it != skipList[count -1].end() && num > (*it).value){
                it++;
            }
            prev = skipList[count -1].insert(it,Node(num, prev));

        }
    }
}

答案

vector<list<Node>> skipList;很危险。如果添加了新列表,则向量可能会重定位所有其他列表,并使所有存储的迭代器无效。即使列表可以在新位置移动构造,它们仍然是新对象,并且将.end()与从另一个对象获得的迭代器进行比较是未定义的行为。

我认为这是你的代码最终会发生的事情。

[可能不是一个正确的答案,但它的评论太长了,我不会调试作者的代码来确保。]

另一答案

一个明显的错误是你的Node类实现。

如果你看看你的Node构造函数只需要一个int,你就无法初始化below迭代器。因此,尝试取消引用below时的任何访问都将导致发生未定义的行为,正如您在此行中所做的那样:

cout << (*(skipList[1].front().below)).value;

如果跳过列表为空,您将看到您的代码将生成Node对象,其中below未初始化。

以下是使用或多或少使用您发布的代码的简单示例:

#include <list>
#include <vector>
#include <iostream>

struct Node // in header file
{
    int value;
    std::list<Node>::iterator below;
    Node(int v, std::list<Node>::iterator b) {
        value = v;
        below = b;
    }
    Node() {}
    Node(int v) {
        value = v;
    }
};

class SkipLists
{
    private:
        std::vector<std::list<Node>> skipList;
    public:
        void insert(int num);
        std::list<Node>::iterator insertPlace(int num, std::list<Node>::iterator it, int height);
        void stack(int num, std::list<Node>::iterator loc);
};

using namespace std;

void SkipLists::insert(int num) 
{
    list<Node>::iterator loc;
    if (skipList.empty()) 
    {
        list<Node> nodes;
        nodes.push_back(Node(num));
        skipList.push_back(nodes);
    }
    else 
    {
        loc = insertPlace(num, skipList[skipList.size() - 1].begin(), skipList.size() - 1);
        skipList[0].insert(loc, Node(num));

    }

    stack(num, loc);

    //this if statement also segfaults
    if (skipList.size() > 1) {
        cout << (*(skipList[1].front().below)).value;
    }
}

list<Node>::iterator SkipLists::insertPlace(int num, list<Node>::iterator it, int height) 
{
    if (height == 0) {
        while (it != skipList[0].end() && skipList[0].size() > 0 && num > (*it).value) 
        {
            it++;
        }
        return it;
    }
    while (it != skipList[height].end() && skipList[height].size() > 0 && num > (*it).value) 
    {
        cout << "he\n";
        it++;
        cout << "lo\n";
    }
    return insertPlace(num, (*it).below, --height);
}

void SkipLists::stack(int num, list<Node>::iterator loc) {
    int flip = rand() % 2;
    int count = 1;
    list<Node>::iterator prev = loc;
    list<Node>::iterator it;
    while (flip == 1) {
        count++;
        flip = rand() % 2;

        if (skipList.size() < count) {
            list<Node> nodes;
            nodes.push_back(Node(num, prev));
            skipList.push_back(nodes);
            prev = skipList[skipList.size() - 1].begin();
        }
        else {
            it = skipList[count - 1].begin();
            while (it != skipList[count - 1].end() && num > (*it).value) {
                it++;
            }
            prev = skipList[count - 1].insert(it, Node(num, prev));
        }
    }
}

// Test    
int main()
{
    SkipLists s;
    s.insert(4);
}

您将看到below未在您运行此非常小的样本时说明您的应用程序崩溃的行上初始化。

Node默认构造函数也存在同样的问题,其中valuebelow成员都未初始化。创建对象时,所有成员都应处于某种有效状态,或者以某种方式为“null”。对于迭代器,由于没有“null”迭代器,因此更难执行此操作,除非您可以将迭代器设置为现有列表的end()迭代器。

基本上你需要设计你的类,以便你确定迭代器指向某个有效的地方,或者指示迭代器不应该被解引用的其他方法。

以上是关于迭代器在添加到添加到列表向量的结构中时会停止指向某个值吗?的主要内容,如果未能解决你的问题,请参考以下文章

使用迭代器在向量中插入下一个值以映射

将带有 glut 的点击坐标添加到向量的链接列表中

For循环迭代不断增长的向量

使用列表和向量工作的不可预测的停止程序

叠加装饰器迭代器

c++迭代器在迭代向量时崩溃