3

So I can get everything to compile, and I understand (I think) how include guards prevent the same definitions from being pulled twice, but what I don't know is if my header file should also include the header file for the class it uses, if my class file already includes it.

Child.hpp

// Child.hpp
#ifndef CHILD_H
#define CHILD_H

class Child {
    public:
        Child();
};

#endif

Child.cpp

// Child.cpp
#include "Child.hpp"

Child::Child() {
    // example
}

Parent.hpp

Should I also include Child.hpp here even though it's already included in Parent.cpp? I understand the header guard would prevent Child being defined twice, but is it considered best practice? Or should I exclusively include Child.hpp here?

// Parent.hpp
#ifndef PARENT_H
#define PARENT_H

class Parent {
    public:
        Parent();
        Parent(Child child);
};

#endif

Parent.cpp

// Parent.cpp
#include "Child.hpp"
#include "Parent.hpp"

int main() {
    Parent parent;
    return 0;
}

Parent::Parent() {
    // example
}

Parent::Parent(Child child) {
    // example
}

We were just given one example in class, and it essentially said that in Parent.cpp it should include Parent.hpp (makes sense) and Child.hpp.

It would seem I would want to include Child.hpp in Parent.hpp as the Parent class relies on the Child class, but either way Child.hpp gets included.

Jens Bodal
  • 1,707
  • 1
  • 22
  • 32
  • 1
    IMHO it's better to forward declare other types needed as much as possible in the header files, and `#include` the headers needed to resolve the forward declarations in the translation units. – πάντα ῥεῖ Feb 15 '15 at 17:19
  • Meaning omit `#include Child.hpp` in `Parent.hpp` and leave it to the implementation of the class (`Parent.cpp`) to include the necessary header files? – Jens Bodal Feb 15 '15 at 17:23
  • @akevit Yes, that is correct. – Cory Kramer Feb 15 '15 at 17:25
  • However, `Parent.hpp` should be self-sufficient, which means (as currently written) either including `Child.hpp` or having a forward declaration of `Child`. End users should be able to include the two headers in any order and still have the code work. – T.C. Feb 15 '15 at 17:37
  • This might need to be a separate question, but if I leave the declaration to exclusively Parent.cpp, and if I put `#include "Child.hpp"` **below** `#include Parent.hpp`, the program will not compile. Should I simply rely on changing the order, or is there something I'm missing? – Jens Bodal Feb 15 '15 at 18:10

1 Answers1

4

If Parent has any instances of Child, yes you must include the header to Child.hpp.

class Parent {
    public:
        Parent();
        Parent(Child child);     // Need full include, complete type used
        Child c;                 // Need full include, complete type used
};

If Parent only had pointers or references to Child you could get away with simply forward-declaring Child, then doing the include in Parent.cpp.

class Child;                     // Forward declaration
class Parent {
    public:
        Parent();
        Parent(Child* child);    
        Child* pc;               // Incomplete type ok if pointer or reference
};

In general, you should avoid including headers inside other headers unless absolutely necessary. At best, it increases build times unnecessarily. At worst, it can lead to circular dependencies.

Cory Kramer
  • 114,268
  • 16
  • 167
  • 218
  • That's incorrect. `Parent(Child child);` does not need a full include, it's just a function declaration. You only need a complete type when you have a `Child` data member or if you are actually defining the function. – T.C. Feb 15 '15 at 17:35
  • @T.C. You are correct. I was (maybe incorrectly?) assuming that since `Parent`'s constructor took a `Child` argument that it was going to have a `Child` instance as a member variable, which is why I said that. Though without seeing the member variables that is just speculation on my part. – Cory Kramer Feb 15 '15 at 17:38
  • I had just put all of this together as a basic example, as it is not being discussed in class and the professor's response was simply "just do it this way". That said, is the **order** of the `#include` directives supposed to matter? If I include Child after Parent it won't compile, but if Child goes above Parent it will. – Jens Bodal Feb 15 '15 at 18:12
  • FWIW, I think the intent of Cory's edit was to show that the function parameter doesn't require a complete definition of `Child` .. though I concede that the resulting design of `Parent` would be a bit strange (`pc` more likely to end up being a dangling pointer than anything, based on common conventions for what `Parent(Child child)` ought to do in such a context) – Asteroids With Wings Jan 22 '21 at 17:12