如何确保创建shared_ptr时自动关联图中顶点与边的双向关系?
我正在构建一个基于Vertex和Edge对象的图存储程序,顶点和边分别存入指针向量中,遵循现代C++规范使用shared_ptr而非裸指针。核心需求是:
- 每条边必须持有两个顶点的指针
- 每个顶点必须维护所有相连边的列表
- 关联关系必须自动建立,不能依赖手动操作
裸指针的实现(参考)
用裸指针时,关联逻辑可以直接写在Edge的构造函数里,创建边的同时自动完成关联:
#include <vector> using namespace std; class Edge; class Vertex { public: Vertex() = default; void addEdge(Edge* p_edge) { m_edges.push_back(p_edge); } private: vector<Edge*> m_edges; }; class Edge { public: Edge(Vertex* p_vertex0, Vertex* p_vertex1): m_vertices{p_vertex0, p_vertex1} { // 创建边时自动关联到顶点 p_vertex0->addEdge(this); p_vertex1->addEdge(this); } private: pair<Vertex*,Vertex*> m_vertices; }; int main() { vector<Vertex*> vertexList; // 初始化顶点列表... // 添加边,自动完成关联 vector<Edge*> edgeList; int index0 = 0, index1 = 1; edgeList.push_back(new Edge(vertexList[index0], vertexList[index1])); }
shared_ptr的痛点
改用shared_ptr后,顶点需要持有shared_ptr<Edge>,但Edge构造函数执行时,对应的shared_ptr还未生成(make_shared或new之后才会创建),无法在构造函数里把自身的shared_ptr传给顶点,只能手动后续关联,这很容易遗漏导致关联失效。
解决方案
方案1:私有构造函数+静态工厂方法
把Edge的构造函数设为私有,强制通过静态工厂方法创建shared_ptr<Edge>,在工厂方法里完成关联操作,确保关联逻辑一定会执行:
#include <memory> #include <vector> #include <utility> using namespace std; class Edge; class Vertex { public: Vertex() = default; void addEdge(shared_ptr<Edge> p_edge) { m_edges.push_back(p_edge); } private: vector<shared_ptr<Edge>> m_edges; }; class Edge { private: // 私有构造函数,禁止直接实例化 Edge(shared_ptr<Vertex> p_vertex0, shared_ptr<Vertex> p_vertex1) : m_vertices{move(p_vertex0), move(p_vertex1)} {} public: // 静态工厂方法,创建边并自动关联顶点 static shared_ptr<Edge> create(shared_ptr<Vertex> v0, shared_ptr<Vertex> v1) { auto edge = make_shared<Edge>(move(v0), move(v1)); edge->m_vertices.first->addEdge(edge); edge->m_vertices.second->addEdge(edge); return edge; } private: pair<shared_ptr<Vertex>, shared_ptr<Vertex>> m_vertices; }; int main() { vector<shared_ptr<Vertex>> vertexList; vertexList.push_back(make_shared<Vertex>()); vertexList.push_back(make_shared<Vertex>()); vector<shared_ptr<Edge>> edgeList; // 只能通过工厂方法创建边,自动完成关联 edgeList.push_back(Edge::create(vertexList[0], vertexList[1])); }
方案2:结合enable_shared_from_this
让Edge继承enable_shared_from_this<Edge>,允许对象获取自身的shared_ptr,但注意构造函数里不能直接调用shared_from_this(),需要配合初始化方法或工厂函数:
#include <memory> #include <vector> #include <utility> using namespace std; class Edge; class Vertex { public: Vertex() = default; void addEdge(shared_ptr<Edge> p_edge) { m_edges.push_back(p_edge); } private: vector<shared_ptr<Edge>> m_edges; }; class Edge : public enable_shared_from_this<Edge> { public: Edge(shared_ptr<Vertex> p_vertex0, shared_ptr<Vertex> p_vertex1) : m_vertices{move(p_vertex0), move(p_vertex1)} {} // 初始化关联,必须在对象被shared_ptr托管后调用 void init() { m_vertices.first->addEdge(shared_from_this()); m_vertices.second->addEdge(shared_from_this()); } private: pair<shared_ptr<Vertex>, shared_ptr<Vertex>> m_vertices; }; // 封装创建+初始化的工具函数 shared_ptr<Edge> make_edge(shared_ptr<Vertex> v0, shared_ptr<Vertex> v1) { auto edge = make_shared<Edge>(move(v0), move(v1)); edge->init(); return edge; } int main() { vector<shared_ptr<Vertex>> vertexList; vertexList.push_back(make_shared<Vertex>()); vertexList.push_back(make_shared<Vertex>()); vector<shared_ptr<Edge>> edgeList; edgeList.push_back(make_edge(vertexList[0], vertexList[1])); }
关键注意点:避免循环引用
如果顶点持有shared_ptr<Edge>,同时边持有shared_ptr<Vertex>,会形成循环引用,导致对象无法被正确释放。解决办法是让其中一方持有weak_ptr,比如将Edge的顶点成员改为:
pair<weak_ptr<Vertex>, weak_ptr<Vertex>> m_vertices;
这样当所有外部的shared_ptr释放后,顶点和边的引用计数会正常归零,避免内存泄漏。
内容的提问来源于stack exchange,提问作者lemmox
相关产品推荐
相关产品推荐

