0

I have gotten many cyclic dependencies recently when my header files includes each other. See also here: What are forward declarations in C++?

I actually do not get totally why its a cycle. When the compiler looks inside the header-file of the include, why does it not recognize the class declaration?

Is there an more elegant/other way to break these cycles instead of a forward declaration of the other class? I do not like that there is a other class declaration in front of my current class. E.g.

#include "Wheel.h"  // Include Wheel's definition so it can be used in Car.
#include <vector>

class Wheel;

class Car
{
    std::vector<Wheel> wheels;
};
muella91
  • 189
  • 1
  • 2
  • 12
  • 1
    Option one is getting rid of cyclic references, option two is to just embrace them, option three is to hide all forward declarations in a header and just include that. – Botje Feb 13 '20 at 09:22
  • `class Wheel` part is not required, because the compiler needs not only declaration but definition either because of `std::vector wheels` so you can't dodge it with forward declaration. – calynr Feb 13 '20 at 09:23
  • It's hard to say without seeing your `"Wheel.h"` file, but if it didn't contain the full `Wheel` class declaration, this would fail to compile, as your `wheels` variable can't use an incomplete type. – Kaldrr Feb 13 '20 at 09:23
  • 2
    You haven't _shown_ a cycle, so that part is hard to answer. And, as already mentioned, your existing forward declaration is redundant, as it comes _after_ you include the full definition (and a fwd decl wouldn't be enough to instantiate `vector` anyway). – Useless Feb 13 '20 at 09:28
  • 1
    I am sorry, but this question cannot be answered here. A true cyclic dependency is when *declaration* of class A would require *definition* of class B while *declaration* of class B also requires *definition* of class A, and is forbidden in C++. Here you have only shown a **direct** dependency. BTW, you need either `#include "Wheel.h` if you need the definition or `class Wheel;` if forward declaration is enough like here. But using both is redundant. – Serge Ballesta Feb 13 '20 at 09:44
  • Sorry, I just used the example from the other topic. But in my application I have cyclic dependencies, so I was curious if there is a better way to solve this. – muella91 Feb 13 '20 at 10:27

2 Answers2

2

I hope I got your stuck point.

To explain this, it is better to examine from the view of compiler.

Compiler does for Car.h:

  • Replaces #include "Wheel.h" with its content.
  • Replaces #include <vector> with its content.

At this step, translation unit for Car.h seems like this.

class Car;

class Wheel
{
    Car* car;
};

// Vector implementation of the stl goes here

class Car
{
    std::vector<Wheel> wheels;
};

At this point, at Car* car; line, class Car declaration (declaration is enough for pointer types) is needed to define class Wheel that's why you need forward declaration because you should tell to compiler that there is a class Car and it will be defined soon.

If you comment out class Car at line 1 compiler could not know whether there will be a class Car or not.

As far as I know there are extra restrictions related with one definition rule, maybe someone else might explain that.

It is not possible to have 'more elegant' way for now.

calynr
  • 1,264
  • 1
  • 11
  • 23
-2

Will can use in each file the directives #ifndef ... #define #endif

So your code will became something like

#ifndef CAR_H
#define CAR_H
#include "Wheel.h"  // Include Wheel's definition so it can be used in Car.
#include <vector>

class Car
{
    std::vector<Wheel> wheels;
};

#endif

Do it in all your header files, so each file will be loaded at most once and the cycles will not happen anymore

  • That doesn't work if there are cyclic dependencies. If there are `Car` usages inside `Wheel.h`, it'd need to forward declare `Car`. – Ted Lyngmo Feb 13 '20 at 09:33
  • #ifdef-guards like this are used to prevent repeated inclusions of a header from leading to multiple definition errors. They can't help with cyclic dependencies in definitions, and cyclic inclusions usually indicate that cyclic dependencies are also present. – Sneftel Feb 13 '20 at 10:05