具有异常处理的函数发生意外的堆损坏

时间:2019-05-21 00:24:31

标签: c++ exception heap-corruption

我正在用C ++编写有向图。如果我使用某个使用异常处理的特定函数初始化它们,则图节点的邻接列表中的图形节点指针会损坏,但是如果使用不使用异常处理的类似函数对其进行初始化,则这些列表指针不会损坏。

我的图类具有以下标题的函数:

bool directed_edge(const Key& parent, const Key& child) throw(std::invalid_argument);

...以及带有此标题的另一个功能:

std::tuple<bool, bool, bool> add_directed_edge(const Key& parent, const Key& child);
如果当前图形中没有directed_edgeparent,则

child会引发异常。 add_directed_edge通过调用directed_edge来工作,并通过将节点实际添加到列表中并然后将它们与边连接来处理异常。

如果使用directed_edge创建边,则根本没有数据损坏-图形节点的邻接表包含预期的数据。但是,如果我使用add_directed_edge,则数据已损坏。这很奇怪,因为add_directed_edge除了调用directed_edge并处理它可能引发的任何潜在错误之外,实际上并没有做很多事情。这使我相信它与函数内部的异常处理有关,但是我不确定。

这是两个功能的实现:

template<typename Key>
bool graph<Key>::directed_edge(const Key& parent, const Key& child) throw(std::invalid_argument)
{
    node* parentItor = find_node(parent);
    node* childItor = find_node(child);

    // Return true if the edge was added
    return parentItor->directed_edge(childItor);
}

template<typename Key>
std::tuple<bool, bool, bool>
graph<Key>::add_directed_edge(const Key& parent, const Key& child)
{
    bool parentAdded;
    bool childAdded;
    bool edgeAdded;

    // Try to add the directed edge.  Exception thrown if either doesn't exist
    try {
        edgeAdded = directed_edge(parent, child);
        return std::make_tuple(false, false, edgeAdded);
    }
    catch(std::invalid_argument& invArg) {
        // Add parent and child, and assign to see if they needed to be added
        parentAdded = add(parent);
        childAdded = add(child);

        // Add the directed edge
        edgeAdded = directed_edge(parent, child);
        return std::make_tuple(parentAdded, childAdded, edgeAdded);
    }
}

我意识到这两个功能都在调用其他功能的 lot ,因此,如果您想了解更多实施细节,可以发表评论,我会尽快与您联系

我用一些基本数据进行了三个测试。在第一个测试中,我手动添加了节点0-9,然后使用directed_edge建立了一些连接。结果是这样的:

0 -> 1, 3
1 -> 2, 4, 6, 7
2 -> 3, 8, 9
3 -> 
4 -> 6, 7, 5
5 -> 
6 -> 7
7 -> 
8 -> 
9 -> 

在第二个测试中,我没有手动向图中添加任何节点。我反复调用add_directed_edge,因为该函数旨在在每次给不存在的节点提供密钥时添加节点。结果是这样的:

0 -> 284985109, 976560249
1 -> 1752440936, 116, 17504392, 7
3 -> 
2 -> 1768366181, 8, 9
4 -> 6, 7, 5
6 -> 7
7 -> 
8 -> 
9 -> 
5 -> 

另外,为了更彻底,我进行了第三次测试,在该测试中,我手动添加了所有节点,然后调用add_directed_edge在先前存在的节点上建立连接。有趣的是,这产生了预期的结果:

0 -> 1, 3
1 -> 2, 4, 6, 7
2 -> 3, 8, 9
3 -> 
4 -> 6, 7, 5
5 -> 
6 -> 7
7 -> 
8 -> 
9 -> 

2 个答案:

答案 0 :(得分:0)

如果您能够使用Valgrind或gcc / clang address-sanitizer之类的代码运行代码,那么它们通常是识别此类问题原因的好方法。

答案 1 :(得分:0)

您猜测问题add_directed_edge中存在异常的假设被很好地猜测,但是是错误的。实际问题是,添加图节点会导致图中图节点的向量调整大小,从而使每个图的邻接表中的图节点指针无效

图类具有成员变量vector<graph_node<Key>> nodes,并且每个图节点具有成员变量vector<graph_node<Key>*> adjacencyList。每当在两个节点之间形成有向边时,您都将执行以下操作:nodes[i].adjacencyList.push_back(&nodes[j])。这将使节点i指向节点j

这是一个问题。每当需要调整向量&nodes[j]的大小时,引用nodes就会失效,因为在调整大小期间,nodes[j]的数据会复制到内存中完全不同的位置。

This网页上有关于容器的更多信息-您应仔细阅读标题为“迭代器无效”的部分。看到我看到的了吗?对于向量,如果调整内部数组的大小,则所有迭代器(即所有指针)都将失效。

如果您坚持要在节点上使用 pointers 的邻接列表,则应使用带有更稳定迭代器的STL容器,例如列表或地图