创建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; }
问题经过
- 最初尝试将图存入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
- 添加
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
相关产品推荐
相关产品推荐

