避免头文件的循环依赖

时间:2011-01-27 13:08:42

标签: c++ software-design architecture

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

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

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

8 个答案:

答案 0 :(得分:48)

如果你有循环依赖,那么你做错了。

例如:

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;
   ...
};

这没关系。

一般规则:

  1. 确保每个标题都可以单独包含。
  2. 如果你可以使用前向声明使用它们!

答案 1 :(得分:15)

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

答案 2 :(得分:7)

我遵循的一些最佳做法是避免循环依赖,

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

答案 3 :(得分:6)

一般方法是将共性分解为第三个头文件,然后由两个原始头文件引用。

另见Circular Dependency Best Practice

答案 4 :(得分:4)

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

#pragma once

#ifndef MY_HEADER_H
#define MY_HEADER_H
your header file
#endif

如果您觉得设计头文件非常无聊,可能会感兴趣的是来自Hwaci(SQLite和化石DVCS的设计者)的makeheaders

答案 5 :(得分:3)

您的目标是 layered approach 。您可以定义模块可以依赖于较低层模块的层,但反之则应该使用 observers 。现在,您仍然可以定义图层的细粒度以及是否接受图层内的循环依赖,但在这种情况下,我会使用this

答案 6 :(得分:3)

通常,头文件应该向前声明,而不是尽可能包含其他头文件。

同时确保每个标题都坚持一个班级。

然后你几乎肯定不会出错。

最差的耦合通常来自膨胀的模板代码。因为你必须在标题中包含定义,它通常会导致必须包含所有类型的标题,然后使用模板的类包括模板标题,包括其他东西的加载。

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

答案 7 :(得分:2)

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