【问题标题】:C++ - Best convention to resolve templated classes' circular dependencies in header files?C++ - 解决头文件中模板类循环依赖的最佳约定?
【发布时间】:2014-07-11 15:46:15
【问题描述】:

我正在用 C++ 编写自己的模板化 Graph 类的实现,因此我也在实现模板化 VertexEdge 类。因此,实现必须在它们各自的头文件中,而不是在单独的.cpp 文件中。 (最佳答案here 对我不起作用)

我的Vertex 类将出边的邻接列表存储为Edges 的vectorEdge 类存储指向源和目标Vertexs 的指针以及权重边缘。

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

这是解决 VertexEdge 类的相互依赖关系的最佳方法吗?用户必须#include 两个文件才能使用其中一个(即Graph.h)。

如果用户想要使用 Vertex 而不必明确包含 Edge.h 怎么办?反之亦然?我应该在另一个里面转发声明和#include吗?

或者我应该在同一个头文件中实现它们?

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

这是我现在拥有的:

Edge.h:

#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

顶点.h:

#ifndef _VERTEX_H__
#define _VERTEX_H__
#include "Edge.h"
#include <vector>

template <class T>
class Vertex
{
private:
    std::vector<Edge<T> > _adj;

...

};
#endif

【问题讨论】:

  • 我不确定让顶点拥有边缘是不是最好的设计。相反,我可能会同时拥有 Graph。
  • 始终努力减少耦合。因为你已经成功了,你就完成了。虽然说如果你需要任何一个,你都需要两者是一个有效的论点,但将它们分开是太多的工作,没有任何收获......
  • 另外,不要使用_EDGE_H__ 等作为包含保护宏的名称。它们是保留的。
  • @T.C.你会推荐什么作为后卫?当 do this 而不是只说 *don't do this 时,它会更有用。
  • @localhost "使用not reserved的名称?

标签: c++ graph header-files circular-dependency


【解决方案1】:

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

由于您首先是在谈论设计Graph 类,因此将两个标题都包含在内部 并没有什么不好。然后,用户只需#include "graph.h"(或您所称的文件),而不真正关心它在较低级别的设计方式。

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

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

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

【讨论】:

  • 我对@9​​87654324@ 的实现正在优化空间速度,因此这种结构是不可取的。
  • @pdpiech 我认为速度不会有太大的不同。我在我的游戏引擎(寻路)中使用了这种结构,它并没有减慢速度(即使地图上有 20 多个单位和 50 个节点)。无论如何 - 除了您所做的之外,没有真正的方法可以解决循环依赖关系。
【解决方案2】:

我通过将Vertex 类与Edge 类解耦,提出了一个折衷的解决方案,如下所示。

Edge.h:

#ifndef EDGE_H
#define EDGE_H

template <class T> class Edge
{
private:
    T* _source;
    T* _dest;
    long long _weight;

...

};
#endif

Vertex.h 中,向量变为std::vector&lt;Edge&lt;Vertex&lt;T&gt; &gt; &gt;。如果需要,可以在Vertex.hGraph.h 中使用公共typedef Edge&lt;Vertex&lt;T&gt; &gt; Edge 来简化这一点。因此,循环依赖被消除,每个类都可以“单独”使用,而无需明确包含另一个类:现在Graph.h 依赖于Vertex.h,而Vertex.h 又依赖于Edge.h

作为一个附注来解决关于拥有其出边的顶点的 cmets:我正在扩展我编写的图形类的已经完整实现,并且算法将是即插即用的(只需很少的更改)我保持相同/相似的邻接列表结构。

【讨论】:

    猜你喜欢
    • 2012-03-09
    • 2012-11-16
    • 1970-01-01
    • 1970-01-01
    • 2013-02-04
    • 2011-07-11
    • 2015-07-25
    • 2022-08-24
    相关资源
    最近更新 更多