list.begin()上的分段错误

时间:2010-09-14 14:29:09

标签: c++ list iterator

我在Folder类中有这个成员函数:

string _recFullPath() {
 list<Folder*> folders;
 list<Folder*>::iterator it = folders.begin();
 folders.push_front(this);
 it = folders.begin();
 while((*it)->hasParent()) {
  folders.push_front((*it)->parent());
  it = folders.begin();
 }
 folders.push_back(this);
 for(it = folders.begin(); it != folders.end(); ++it) {
  cout << (*it)->getName() << "/";
 }
}

这会编译,但是当涉及到it = folders.begin()时,在while循环中它会产生分段错误,我无法弄清楚原因。 Folder对象的布局如下:

class Folder {
  private:
    Folder* _parent;
    string _name;
    string _fullPath;
    string _recStrFullPath;
    bool _hasParent;

  public:
    Folder(string name) {
        this->_name = name;
        this->_hasParent = false;
    }

    Folder(string name, Folder* parent) {
        this->_parent = parent;
        this->_name = name;
        this->_hasParent = true;
    }

    Folder* parent() {
        return this->_parent;
    }

    string getName() {
        return this->_name;
    }

};

当然还有上面提到的功能。有人可以在上面的代码中看到我做错了什么吗?

2 个答案:

答案 0 :(得分:1)

我不知道为什么你的while循环完全使用迭代器。这将更清洁,更简单:

list<Folder*> folders;
Folder* current = this;

while (current->hasParent()) {
    folders.push_front(current);
    current = current.parent();
}

folders.push_front(current);

for(list<Folder*>::const_iterator i = folders.begin(); i != folders.end(); ++i) {
    cout << (*i)->getName() << "/";
}

答案 1 :(得分:0)

在不管理该类存储的情况下,要求在类中使用指针通常不是好的形式。至少,你必须清楚地说明调用者的分配要求应该如何工作。以下是一些代码来说明:

Folder foo(){
    Folder bar("bar");
    Folder baz("baz", &bar);
    return baz;
}

这里发生的事情非常难看,但看起来你做了你应该做的事情。返回时发生的事情是baz被复制到调用者需要去的存储位置,但是baz保留了指向bar的指针。 bar(和原来的baz,你现在有一个副本)都没了,在函数结束时从堆栈中释放出来。

有几种方法可以摆脱这种混乱局面。正确的方法可能是在类本身中完全管理内存。这是另一个版本:

class Folder {
  private:
    Folder* _parent;
    string _name;
    string _fullPath;
    string _recStrFullPath;
    bool _hasParent;

  public:
    Folder(const Folder & src)
        : _name(src._name), _fullPath(src._fullPath)
        , _recStrFullPath(src._recStrFullPath)
        {
        if (src._parent) {
            _parent = new Folder(src._parent);
        }
    }
    ~Folder() {
        delete _parent;
    }

    Folder(string name) {
        this->_name = name;
        this->_hasParent = false;
    }

    Folder(string name, const Folder & parent) {
        this->_parent = new Folder(parent);
        this->_name = name;
        this->_hasParent = true;
    }

    Folder* parent() {
        return this->_parent;
    }

    string getName() {
        return this->_name;
    }

};

重要的变化是,在创建子节点时,实际上将父节点复制到子节点中,而不是指针。孩子有自己的副本。它还负责该副本,因此调用者根本不需要关心它。

为了完成这项工作,必须对课程进行一些更改。更改了子构成构造函数的调用签名,以明确父级不受影响。填写_parent时,将使用new创建副本。

为了方便这一点,有必要添加另一个构造函数,一个复制构造函数,因为我们需要特别关注_parent节点。

最后,由于我们在类本身中执行这些alloc,因此在实例消失时添加析构函数来清理这些alloc是必要的。

现在,来电者可以这样:

Folder foo(){
    Folder bar("bar");
    Folder baz("baz", bar);
    return baz;
}

礼貌地工作。