C++ 避免头文件的循环依赖关系

C++ 避免头文件的循环依赖关系,c++,software-design,architecture,C++,Software Design,Architecture,关于如何避免头文件的循环依赖,您有什么好的建议吗 当然,从一开始,我就尝试将项目设计得尽可能透明。然而,随着越来越多的特性和类被添加,项目变得不那么透明,循环依赖开始发生 是否有任何通用的、经过验证的和有效的规则?谢谢。取决于您的预处理器功能: #pragma once 或 如果您觉得设计头文件非常枯燥,那么您可能会对来自Hwaci(SQLite和fossil DVCS的设计者)的头文件感兴趣。一般的方法是将通用性分解到第三个头文件中,然后由两个原始头文件引用 另请参见如果存在循环依赖,则说明

关于如何避免头文件的循环依赖,您有什么好的建议吗

当然,从一开始,我就尝试将项目设计得尽可能透明。然而,随着越来越多的特性和类被添加,项目变得不那么透明,循环依赖开始发生


是否有任何通用的、经过验证的和有效的规则?谢谢。

取决于您的预处理器功能:

#pragma once


如果您觉得设计头文件非常枯燥,那么您可能会对来自Hwaci(SQLite和fossil DVCS的设计者)的头文件感兴趣。

一般的方法是将通用性分解到第三个头文件中,然后由两个原始头文件引用


另请参见

如果存在循环依赖,则说明您做错了什么

例如:

foo.h
-----
class foo {
public:
   bar b;
};

bar.h
-----
class bar {
public:
   foo f;
};
您可能想要:

foo.h
-----
class bar; // forward declaration
class foo {
   ...
   bar *b;
   ...
};

bar.h
-----
class foo; // forward declaration
class bar {
   ...
   foo *f;
   ...
};
这没关系

一般规则:

  • 确保每个标题都可以单独包含
  • 如果可以使用正向声明,请使用它们
    你的目标是一个。您可以定义模块可以依赖于较低层模块的层,但应使用进行反向定义。现在,您仍然可以定义层的细粒度以及是否接受层内的循环依赖关系,但在本例中,我将使用。

    • 尽可能使用转发声明
    • 如果cpp文件只需要任何头包含,则将其从头文件移出并移入相应的cpp文件。最简单的方法是将
      #include“myclass.h”
      作为
      myclass.cpp
      中的第一个include
    • 在独立类之间的交互点引入接口有助于减少依赖性

    在一般情况下,头文件应主动声明,而不是尽可能包含其他头文件

    还要确保每个标题只包含一个类

    那么你几乎肯定不会出错

    最糟糕的耦合通常来自臃肿的模板代码。因为必须在头中包含定义,所以通常需要包含所有类型的头,然后使用模板的类包含模板头,包括一堆其他内容


    出于这个原因,我通常会说:小心使用模板!理想情况下,模板不必在其实现代码中包含任何内容。

    我为避免循环依赖性而遵循的一些最佳实践是

  • 坚持OOAD原则。不要包含头文件,除非包含的类与当前类具有组合关系。改为使用转发声明
  • 设计抽象类作为两个类的接口。通过该接口进行类的交互

  • 尽管Artyom提供了最佳答案,但本教程也很棒,并提供了一些扩展

    这与其说是为了避免循环依赖,不如说是为了避免“重新定义符号”错误。这是一个标准的,绝对需要的练习。你好,贝诺德,是的,我同意彼得·托洛克的观点。这在每本教科书和一个必须使用的实践中都有解释。非常感谢您的回复。+1嗨,阿蒂奥姆,谢谢您的回复。更频繁地使用转发声明可能会有所帮助。@Artyom:如果指针是用来拥有资源的,我建议使用
    作用域\u ptr
    唯一\u ptr
    。如果指针仅仅是一个对象的引用,那么可能有必要使用观察者模式,以便在被引用对象被销毁时它“未设置”。@Matthieu M.当然,(或者
    auto\u ptr
    ,当您不想依赖
    boost
    C++0x
    时,这会更好)。但我更想展示总体思路,而不是现场代码。它甚至可能是
    std::vector
    ,如果foo有前向声明,它会起作用。@Artyom:不,不要使用
    auto\u ptr
    ,它更糟糕。如果需要,可以从boost中删除
    作用域ptr
    的代码,但是
    自动ptr
    会带来太多惊喜(在复制/分配时)。但是如果我们使用*b指针的某些方法?那么我们就不能把它包括进去。那怎么办呢?这里展示了我们可以内联这些函数,但它看起来不像是一个好的通用方法+1hi-Ed,这是另一个非常好的建议。谢谢。我查看了你提供的链接,它显示了重新设计分类以避免循环依赖的好例子。+1你好,Jon,谢谢你的回复。上面已经提到了你的一些建议,但是,始终将heade文件包含到.cpp文件中而不是.h文件中的方法是新的和有用的。我认为这个答案更好地解决了如何避免循环依赖的编译错误的问题,同时避免了因为必须处理循环依赖而犯错误的咒语。如果您使用的是GoF设计模式和复杂性,那么在某一点上您将具有循环依赖性。最好的建议不仅仅是前瞻性声明(这过于简化了解决方案),而是要点2。第二个建议是我一直在寻找的+1 hello Stefaanv,分层方法对我来说非常新,看起来需要大量准备和重新设计。这是非常有价值的建议。谢谢。分层方法是一个很好的想法,特别是因为它不是C++特定的,因此在很多情况下都是有价值的:+ 1 Arun,尤其是使用抽象/接口类的第二个建议是有用的。我要试一试。谢谢。+1嗨,现金牛,老实说,我没有太注意转发声明。相反,我使用了“包含”。非常感谢你的回答。
    foo.h
    -----
    class bar; // forward declaration
    class foo {
       ...
       bar *b;
       ...
    };
    
    bar.h
    -----
    class foo; // forward declaration
    class bar {
       ...
       foo *f;
       ...
    };