C++ - 解决头文件中模板化类循环依赖关系的最佳约定?

C++ - Best convention to resolve templated classes' circular dependencies in header files?

本文关键字:关系 依赖 循环 最佳 约定 解决 文件 C++      更新时间:2023-10-16

我正在C++编写自己的模板化Graph类的实现,所以我也在实现模板化VertexEdge类。 因此,实现必须位于各自的头文件中,而不是单独的.cpp文件中。 (这里的首要答案对我不起作用(

我的 Vertex 类将外边的邻接列表存储为 Edge s 的vectorEdge 类存储指向源和目标 Vertex s 的指针以及边的权重。

由于Edge只存储指向Vertex的指针,因此在Edge.h中转发声明Vertex类就足够了。 然后我可以#include "Edge.h" Vertex.h的顶部.

这是解决Vertex类和Edge类的共同依赖关系的最佳方法吗?用户必须#include两个文件才能使用任何一个文件(即在Graph.h中(。

如果用户想要使用Vertex而不必显式包含Edge.h反之亦然,该怎么办? 我应该转发声明并在另一个内部#include吗?

还是应该在同一个头文件中实现它们?

如果有的话,STL如何解决这个问题?

这是我现在拥有的:

边缘:

#ifndef _EDGE_H__
#define _EDGE_H__
template <class T> class Vertex;
template <class T>
class Edge
{
private:
    Vertex<T>* _source;
    Vertex<T>* _dest;
    unsigned long long _weight;
...
};
#endif

Vertex.h:

#ifndef _VERTEX_H__
#define _VERTEX_H__
#include "Edge.h"
#include <vector>
template <class T>
class Vertex
{
private:
    std::vector<Edge<T> > _adj;
...
};
#endif

如果用户想要使用顶点而不必显式包含 Edge.h 或反之亦然,该怎么办?

由于您本质上是在谈论设计一个Graph类,因此将两个标头包含在其中并没有什么不好。然后,用户只会#include "graph.h"(或任何你称之为文件的东西(,而不是真正关心它在较低级别上是如何设计的。

话虽如此 - 如果你想让你的类设计保持目前的方式 - 你的解决方案是一个好主意。但是,正如评论中已经提到的,顶点不需要拥有其边缘。也许你应该考虑一个不同的结构:

template<typename vertexType, typename edgeType>
class graph{
    std::map<std::pair<vertexType,vertexType>, edgeType>;
};

请记住,使用此设计,特定方法的编写可能会更复杂(并且可能更慢(。但是 - 您最终会在此过程中使用更少的内存。根据你真正想要实现的目标(小尺寸或快速工作(,你最终可能会使用不同的图形结构。

我提出了

一个解决方案的折衷方案,将Vertex类与Edge类分离,如下所示。

边缘:

#ifndef EDGE_H
#define EDGE_H
template <class T> class Edge
{
private:
    T* _source;
    T* _dest;
    long long _weight;
...
};
#endif

Vertex.h中,向量变得std::vector<Edge<Vertex<T> > >.如果需要,可以通过Vertex.hGraph.h的公共typedef Edge<Vertex<T> > Edge来简化这一点。 因此,消除了循环依赖性,每个类都可以"单独"使用,而无需显式包含另一个类:现在Graph.h取决于Vertex.h取决于Edge.h

作为解决有关拥有其外边缘的顶点的评论的旁注:我正在扩展我编写的图形类的已经完整的实现,并且只有当我保持相同/相似的邻接列表结构时,算法才会即插即用(更改最少(。