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

如何确保创建shared_ptr时自动关联图中顶点与边的双向关系?

用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 04:01:16