You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

创建Boost Graph Library图的vector触发段错误,求解决

Boost Graph复制时触发段错误的解决方法

背景代码

顶点/边类定义

class Logic
{
private:
    std::string name;
    ivl_net_logic_t log;

public:
    Logic(ivl_net_logic_t L);
    ~Logic();

    ivl_net_logic_t getLogic() { return log; }
    void setName(std::string &n) { name = n; }
    std::string getName() { return name; }
};

class Signal
{
private:
    ivl_signal_t signal;

public:
    Signal(ivl_signal_t sig);
    /* Signal(); */
    ~Signal();

    ivl_signal_t getSignal();
};

Boost Graph类型定义

#include <boost/config.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/graph/topological_sort.hpp>
#include <boost/graph/dominator_tree.hpp>
#include <boost/iostreams/operations.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/graph/graphml.hpp>
#include <boost/graph/copy.hpp>

typedef typename boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS, Logic, Signal> Graph;
typedef typename boost::graph_traits<Graph>::vertex_descriptor Vertex;
typedef typename boost::graph_traits<Graph>::edge_descriptor Edge;
typedef typename boost::graph_traits<Graph>::vertex_iterator VertexIterator;
typedef typename boost::graph_traits<Graph>::edge_iterator EdgeIterator;

图打印函数

void printGraph(Graph &g)
{
    std::cout << "num_vertices(g) = " << num_vertices(g) << std::endl;
    std::cout << "num_edges(g) = " << num_edges(g) << std::endl;

    std::cout << "Vertices = [ ";
    VertexIterator vi, vend;
    for (boost::tie(vi, vend) = vertices(g); vi != vend; ++vi)
        std::cout << g[*vi].getName() << " ";
    std::cout << "]" << std::endl;

    std::cout << "Edges = [" << std::endl;
    EdgeIterator ei, end;
    for (boost::tie(ei, end) = edges(g); ei != end; ++ei)
    {
        Vertex s = source(*ei, g);
        Vertex t = target(*ei, g);
        std::cout << g[s].getName() << " --> " << g[t].getName() << std::endl;
    }
    std::cout << "]" << std::endl;
}

问题经过

  1. 最初尝试将图存入vector:
Graph g;
// 向g中添加顶点和边
printGraph(g);

std::vector<Graph> graph_tracks = {g};

触发编译错误,提示Signal缺少无参构造函数:

/usr/include/boost/graph/detail/adjacency_list.hpp:2190:43: error: no matching function for call to ‘Signal::Signal()’
 2190 |       typename Config::edge_property_type p;
      |                                           ^
includes/Signal.hpp:17:2: note: candidate: ‘Signal::Signal(ivl_signal_t)’
   17 |  Signal(ivl_signal_t sig);
      |  ^~~~~~
includes/Signal.hpp:17:2: note:   candidate expects 1 argument, 0 provided
includes/Signal.hpp:8:7: note: candidate: ‘Signal::Signal(const Signal&)’
    8 | class Signal
      |       ^~~~~~
includes/Signal.hpp:8:7: note:   candidate expects 1 argument, 0 provided
  1. 添加Signal的无参构造函数后,修改代码为:
std::vector<Graph> graph_tracks;
graph_tracks.push_back(g);

代码可编译,但执行push_back时触发段错误。

问题原因

  • 无参构造函数的强制要求:Boost Graph的adjacency_list在执行复制、扩容等操作时,要求顶点/边的属性类型必须是默认可构造的(即有无参构造函数),否则无法创建临时属性对象。
  • 段错误的核心原因:Logic和Signal类使用了编译器生成的默认拷贝构造函数,属于浅拷贝。而ivl_net_logic_t和ivl_signal_t是Icarus Verilog的内部资源句柄(本质是指针或引用),浅拷贝会导致多个对象共享同一资源。当原对象析构时,资源被释放,拷贝后的对象访问已释放的内存,触发段错误。

解决方法

1. 正确实现无参构造函数

为Logic和Signal添加无参构造,初始化内部句柄为合法空值(根据Icarus Verilog的API调整):

// Signal类
Signal() : signal(nullptr) {} // 若ivl_signal_t是指针类型,用nullptr初始化

// Logic类
Logic() : log(nullptr) {} // 同理初始化ivl_net_logic_t为空

2. 实现深拷贝构造与赋值运算符

如果Icarus Verilog提供了复制资源的API,为类实现深拷贝逻辑,避免共享资源:

// Signal类拷贝构造
Signal(const Signal& other) {
    // 示例:调用Icarus的复制API(需确认实际存在的函数)
    // signal = ivl_signal_dup(other.signal);
}

// Signal类赋值运算符
Signal& operator=(const Signal& other) {
    if (this != &other) {
        // 先释放当前资源(若需要)
        // ivl_signal_free(signal);
        // 复制对方资源
        // signal = ivl_signal_dup(other.signal);
    }
    return *this;
}

// Logic类同理
Logic(const Logic& other) : name(other.name) {
    // log = ivl_net_logic_dup(other.log);
}

Logic& operator=(const Logic& other) {
    if (this != &other) {
        name = other.name;
        // ivl_net_logic_free(log);
        // log = ivl_net_logic_dup(other.log);
    }
    return *this;
}

3. 改用移动语义(无需保留原对象时)

如果push_back后不需要再使用原Graph g,可以用移动构造避免拷贝:

graph_tracks.push_back(std::move(g));

移动操作会转移资源所有权,不会复制顶点和边的属性,避免浅拷贝问题。

4. 改用指针存储(资源不可复制时)

若Icarus的句柄不允许复制,可改用指针容器存储图对象:

std::vector<std::unique_ptr<Graph>> graph_tracks;
graph_tracks.emplace_back(std::make_unique<Graph>(std::move(g)));

通过指针管理对象生命周期,避免直接拷贝图。

额外提示

  • 确认Icarus Verilog API中,ivl_net_logic_t和ivl_signal_t的生命周期管理规则,是否需要手动释放资源。
  • 若资源不可复制,应尽量避免拷贝Graph对象,优先使用移动或指针语义。

内容的提问来源于stack exchange,提问作者esthée

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 22:57:22