【问题标题】:Trashes in copied std::list复制的 std::list 中的垃圾
【发布时间】:2013-06-17 13:50:11
【问题描述】:

我的图形类看起来像:

class Graph {
  public:
    typedef unsigned int size_type;
    typedef std::list<size_type> Neighbours;

  protected:
    size_type m_nodes_count, m_edges_count;

  public:
    Graph(size_type nodes_count = 0) :
      m_nodes_count(nodes_count), m_edges_count(0) {}

    virtual bool is_edge(size_type from, size_type to) = 0;
    virtual Neighbours neighbours(size_type node) = 0;

    virtual Graph& add_edge(size_type from, size_type to) = 0;
    virtual void delete_edge(size_type from, size_type to) = 0;

    size_type nodes_count() { return m_nodes_count; }
    size_type edges_count() { return m_edges_count; }

    virtual ~Graph() {}
};

class AdjList : public Graph {
  private:
    typedef std::list<size_type> Row;
    std::vector<Row> m_list;

  public:
    AdjList(size_type nodes_count) : Graph(nodes_count) {
      m_list.resize(nodes_count);
    }

    AdjList(const AdjList& g) : AdjList(g.m_nodes_count) {
      for (int i = 0; i < nodes_count(); i++)
        std::copy(g.m_list[i].begin(), g.m_list[i].end(), std::back_inserter(m_list[i]));
    }

    virtual bool is_edge(size_type from, size_type to) override {
      return std::find(m_list[from].begin(), m_list[from].end(), to) != m_list[from].end();
    }

    virtual Graph& add_edge(size_type from, size_type to) override {
      if (!is_edge(from, to) && !is_edge(to, from)) {
        m_list[from].push_back(to);
        m_list[to].push_back(from);
        m_edges_count++;
      }

      return *this;
    }

    virtual void delete_edge(size_type from, size_type to) override {
      m_list[from].remove(to);
      m_list[to].remove(to);
      m_edges_count--;
    }

    virtual Neighbours neighbours(size_type node) {
      return m_list[node];
    }
};

但是当我尝试获取 graph.neighbours(v) 时,我得到了大量的垃圾:

(gdb) p graph
$1 = {<Graph> = {_vptr.Graph = 0x406210 <vtable for AdjList+16>, m_nodes_count = 3, m_edges_count = 3}, m_list = std::vector of length 3, capacity 3 = {std::list = {[0]
 = 2, [1] = 1},
    std::list = {[0] = 0, [1] = 2}, std::list = {[0] = 0, [1] = 1}}}
(gdb) p graph.neighbours(0)
$2 = std::list = {[0] = 2, [1] = 1, [2] = 4294956560, [3] = 2, [4] = 1,
  [5] = 4294956560, [6] = 2, [7] = 1, [8] = 4294956560, [9] = 2, [10] = 1,
  [11] = 4294956560, [12] = 2, [13] = 1, [14] = 4294956560, [15] = 2,
  [16] = 1, [17] = 4294956560, [18] = 2, [19] = 1, [20] = 4294956560,
  [21] = 2, [22] = 1, [23] = 4294956560, [24] = 2, [25] = 1,...

如何解决?

【问题讨论】:

  • Neighbours 是什么(看起来是 typedef,但要确定)
  • 另外,gdb 漂亮的打印机会不会有什么不寻常的地方?如果您运行将结果分配给局部变量的代码,它会产生相同的结果吗?
  • 然后打印好。谢谢。但它仍然不能解决我的工作:/但是谢谢。
  • 好吧,如果它在赋值后打印正确,那可能是GDB中的一个错误,返回值的析构函数在打印之前执行,导致垃圾数据。
  • 您能否提供初始化图形的代码?一直到g.neighbours(0) 电话,请

标签: c++ list stl gdb circular-list


【解决方案1】:

gdb 可能被 实现细节 std::list 弄糊涂了。例如。旧的SGI STL list 被实现为循环列表。在list 对象内,只有一个名为_M_node_List_node&lt;_Tp&gt; 指针。构造函数将最终节点元素的内部_M_next 指针设置为等于_M_node 本身。

std::list 的标准库实现使用这种循环实现的原因是为了避免最终元素的特殊情况(例如,它们也可以使用带有 nullptr 下一个指针的哨兵元素)。 Matt Austern 对此有一个很好的ACCU presentation(但该链接当前指向损坏的文件,请参阅archived version here)。

这个循环实现解释了为什么g.neighbors()gdb 输出具有[0] = 2, [1] = 1, [2] = 4294956560, /* etcetera */ 的重复模式。值 4294956560 只是你的std::list 的内部_M_node 变量的内存地址,所以如果gdb 只做简单的指针追逐,它会混淆。请注意,它小于2^32,即您可能正在为 32 位编译它。

您可能应该在您自己的系统标准库的&lt;list&gt; 标头中验证这一点。 gdb 的错误报告也可能需要。

【讨论】:

  • 这让我想知道为什么在这种情况下,上述 std::list 实例的输出有效......
  • 谁知道呢,调试器可能会限制显示和截断的元素数量。
【解决方案2】:

我认为问题出在复制构造函数上,只需这样做:

AdjList(const AdjList& g) : AdjList(g.m_nodes_count) {
    m_list = g.m_list ; 
}

operator=() 方法应该创建一个新的Vector&lt;Row&gt;,它与g.m_list 具有相同的节点。

【讨论】:

    猜你喜欢
    • 2019-07-30
    • 2018-11-20
    • 1970-01-01
    • 2020-03-06
    • 2016-10-22
    • 2010-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多