55

Do you have any good advice on how to avoid circular dependencies of header files, please?

Of course, from the beginning, I try to design the project as transparent as possible. However, as more and more features and classes are added, and the project gets less transparent, circular dependencies start happening.

Are there any general, verified, and working rules? Thanks.

Bunkai.Satori
  • 4,350
  • 12
  • 43
  • 74

8 Answers8

56

If you have circular dependency then you doing something wrong.

As for example:

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

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

Is illegal you probably want:

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

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

And this is ok.

General rules:

  1. Make sure each header can be included on its own.
  2. If you can use forward declarations use them!
Artyom
  • 30,091
  • 20
  • 121
  • 208
  • +1 Hi Artyom, thanks for the reply. more frequent usage of forward declarations might be helpful. – Bunkai.Satori Jan 27 '11 at 13:49
  • @Artyom: if the pointer is meant to own the resource, I'd advise using a `scoped_ptr` or `unique_ptr`. If the pointer is merely a reference to an object, then it could be necessary to use an Observer pattern so that it's "unset" whenever the referenced object is destroyed. – Matthieu M. Jan 27 '11 at 14:16
  • @Matthieu M. Of course, (or `auto_ptr` which is better when you don't want to be dependent on `boost` or `C++0x`). But I rather wanted to show general idea rather live code. It may even be `std::vector` which would work if foo has forward declaration. – Artyom Jan 27 '11 at 14:19
  • @Artyom: no, don't use `auto_ptr`, it's worse. scrap off the code of `scoped_ptr` from boost if you need to, but `auto_ptr` brings too many surprises (on copy / assign). – Matthieu M. Jan 27 '11 at 14:56
  • @Matthieu if you work with current C++ standard it is only smart pointer there and it works as `scoped_ptr` most of the time (of course you should be aware of its move semantics that is very nice when you know how to use it). – Artyom Jan 27 '11 at 17:35
  • @Artyom: I know, but boost license is very permissive so you can (mostly) just rip the `scoped_ptr` out of boost (or even reimplement it yourself, it's quite easy... just beware of exceptions ^^) and you'll have a noncopyable smart pointer. – Matthieu M. Jan 27 '11 at 18:45
  • @Matthieu Nothing wrong with `auto_ptr` it was fine and it is sill fine. And in some cases it is very good to define move policy like returning `auto_ptr` from function. It was deprecated in flavor of `unique_ptr` only because C++0x ans rvalue reference which does not exists in C++98 and allows to do things safer. Also please note, the standard had not accepted `scoped_ptr` in it. – Artyom Jan 27 '11 at 19:28
  • @Artyom: I've never liked `auto_ptr` because of its screwed concept of "copy", which is usually surprising... I know that we "needed" it somewhat for returning pointers from factory, but whenever a copy is not necessary, or not wanted. `scoped_ptr` is better in this regard, for C++03; it's also completely unnecessary once you get a `unique_ptr` :) – Matthieu M. Jan 27 '11 at 19:44
  • 1
    But if we are using some methods of the *b pointer ? Then we cannot forward include it. What to do then ? Here is showed that we can inline these function http://www.cplusplus.com/forum/articles/10627/ but it doesn't look like a good general approach – rank1 Apr 17 '13 at 10:54
19
  • Use forward declarations where possible.
  • Move any header includes out of a header file and into the corresponding cpp file if they are only needed by the cpp file. Easiest way to enforce this is to make the #include "myclass.h" the first include in myclass.cpp.
  • Introducing interfaces at the point of interaction between separate classes can help reduce dependencies.
jon-hanson
  • 7,193
  • 2
  • 32
  • 57
  • 1
    +1 Hello Jon, thanks for your reply. Some your advices were already mentioned above, but the one to always #include heade files into .cpp files instead .h files was new and helpful. – Bunkai.Satori Jan 27 '11 at 13:54
  • I think this answer better addresses the question about how to avoid compilation errors with circular dependencies while avoiding the mantra that you did something wrong because you have to deal with a circular dependency. If you're working with GoF design patterns and complexity you WILL have a circular dependency at some point. The best advice is not just forward declaration (that oversimplifies the solution), but bullet point #2. –  Sep 05 '14 at 17:36
  • Second suggestion is what I was looking for – rluks Jan 24 '16 at 21:20
8

Some best practices I follow to avoid circular dependencies are,

  1. Stick to OOAD principles. Don't include a header file, unless the class included is in composition relationship with the current class. Use forward declaration instead.
  2. Design abstract classes to act as interfaces for two classes. Make the interaction of the classes through that interface.
Sulla
  • 6,763
  • 7
  • 40
  • 65
7

A general approach is to factor out the commonalities into a third header file which is then referenced by the two original header files.

See also Circular Dependency Best Practice

Community
  • 1
  • 1
Ed Guiness
  • 33,233
  • 16
  • 102
  • 141
4

depending on your preprocessor capabilities:

#pragma once

or

#ifndef MY_HEADER_H
#define MY_HEADER_H
your header file
#endif

If you find it very boring to design header files maybe makeheaders from Hwaci (designers of SQLite and fossil DVCS) could be of interest for you.

Benoit
  • 70,220
  • 21
  • 189
  • 223
  • 3
    This is not so much to avoid circular dependencies, as to avoid "redefinition of symbol" errors. It is a standard, absolutely needed practice nevertheless. – Péter Török Jan 27 '11 at 13:19
  • Hello Benoid, yeah, I have to agree with Peter Torok. This something explained in every textbook and in a must-use pratcice. Thank you very much, for your response. – Bunkai.Satori Jan 27 '11 at 13:59
4

What you're aiming at is a layered approach. You can define layers where modules can depend on lower layer modules but the inverse should be done with observers. Now you can still define how fine-grained your layers should be and whether you accept circular dependency within layers, but in this case I would use this.

Community
  • 1
  • 1
stefaanv
  • 12,981
  • 2
  • 27
  • 48
  • +1 hello Stefaanv, the layered approach is quite new to me, and looks like something that requires a lot of preparations and redesign. It is very valuable advice. Thank you. – Bunkai.Satori Jan 27 '11 at 13:56
  • The layered approach is a great idea, notably because it's not C++ specific and is therefore valuable in a lot of situations :) – Matthieu M. Jan 27 '11 at 14:18
3

In general header files should forwardly declare rather than include other headers wherever possible.

Also ensure you stick to one class per header.

Then you almost certainly will not go wrong.

The worst coupling usually comes from bloated template code. Because you have to include the definition inside the header, it often leads to all kinds headers having to be included, and then the class that uses the template includes the template header, including a load of other stuff.

For this reason, I would generally say: be careful with templates! Ideally a template should not have to include anything in its implementation code.

CashCow
  • 29,087
  • 4
  • 53
  • 86
  • +1 Hi CashCow, to be honest, I did not pay too much attention to forward declarations. Instead I used #include. Thank you very much, for this answer. – Bunkai.Satori Jan 27 '11 at 14:09
2

Altough Artyom provided best answer this tutorial is also great and provides some extenstions http://www.cplusplus.com/forum/articles/10627/

rank1
  • 910
  • 3
  • 14
  • 33