views:

1317

answers:

6

Hello!

I'm a C++ newbie, but I wasn't able to find the answer to this (most likely trivial) question online. I am having some trouble compiling some code where two classes include each other. To begin, should my #include statements go inside or outside of my macros? In practice, this hasn't seemed to matter. However, in this particular case, I am having trouble. Putting the #include statements outside of the macros causes the compiler to recurse and gives me "#include nested too deeply" errors. This seems to makes sense to me since neither class has been fully defined before #include has been invoked. However, strangely, when I try to put them inside, I am unable to declare a type of one of the classes, for it is not recognized. Here is, in essence, what I'm trying to compile:

A.h

#ifndef A_H_
#define A_H_

#include "B.h"

class A
{
    private:
     B b;

    public:
     A() : b(*this) {}
};

#endif /*A_H_*/

B.h

#ifndef B_H_
#define B_H_

#include "A.h"

class B
{
    private:
          A& a;

    public:
     B(A& a) : a(a) {}
 };

#endif /*B_H_*/

main.cpp

#include "A.h"

int main()
{
    A a;
}

If it makes a difference, I am using g++ 4.3.2.

And just to be clear, in general, where should #include statements go? I have always seen them go outside of the macros, but the scenario I described clearly seems to break this principle. Thanks to any helpers in advance! Please allow me to clarify my intent if I have made any silly mistakes!

A: 

I doubt that this can be done. You're not talking about calling two functions from inside each other recursively, but rather of putting two objects one inside the other recursively. Think about putting a house with a picture of a house with a picture of a house etc... This will take up an infinite amount of space because you'll have an infinite number of houses and pictures.

What you can do is have each of A and B include either pointers or references to each other.

Nathan Fellman
True, except that in the code he posted, A.b is a reference to B, so it's just having a house inside a house, and the address of the outer house inside the inner one. Which takes finite space. ;)
jalf
Er, the other way around, of course. B.a is a reference to A.
jalf
hmmm... you're right. I missed that detail.
Nathan Fellman
+5  A: 

And just to be clear, in general, where should #include statements go?

Inside the include guards, for the reason you mentioned.

For your other problem: you need to forward-declare at least one of the classes, e.g. like this:

#ifndef B_H_
#define B_H_

// Instead of this:
//#include "A.h"

class A;

class B
{
    private:
            A& a;

    public:
            B(A& a) : a(a) {}
 };

#endif /*B_H_*/

This only works for declarations though: as soon as you really use an instance of A, you need to have defined it as well.

By the way, what Nathan says is true: you can't put class instances into each other recursively. This only works with pointers (or, in your case, references) to instances.

Konrad Rudolph
Note that you still can't have the classes include each other. If A includes B then B can't include A
Nathan Fellman
Notice that class B merely contains a reference to its corresponding class A object. I don't think that there is anything wrong with what I'm trying to do.
Scott
+1  A: 

Oops! I think I found a solution that involves putting the #include statements inside the class and using a forward declaration. So, the code looks like:

#ifndef A_H_
#define A_H_

class B;

#include "B.h"

class A
{
    private:
            B b;

    public:
            A() : b(*this) {}
};

#endif /*A_H_*/

And similarly for class B. It compiles, but is this the best approach?

Scott
That''ll work, but you'll generally want your #includes before any other code (except the include guards), so move the forward declaration of B down after the #include and all should be well. And yes, forward declarations are the standard way to resolve this.
jalf
No this is not the solution, You have forward declared B and then also included B.h which obviates the forward declare. It is A you want to forward declare in B.h with no include of A.h. Inorder to have an instance of B in A you do have to include B.h. Konrad has the right answer.
Roger Nelson
+8  A: 

By "the macros" I assume you mean the #ifndef include guards? If so, #includes should definitely go inside. This is one of the major reasons why include guards exists, because otherwise you easily end up with an infinite recursion as you noticed.

Anyway, the problem is that at the time you use the A and B classes (inside the other class), they have not yet been declared. Look at what the code looks like after the #includes have been processed:

//#include "A.h" start
#ifndef A_H_
#define A_H_

//#include "B.h" start
#ifndef B_H_
#define B_H_

//#include "A.h" start
#ifndef A_H_ // A_H_ is already defined, so the contents of the file are skipped at this point
#endif /*A_H_*/

//#include "A.h" end

class B
{
    private:
            A& a;

    public:
            B(A& a) : a(a) {}
 };

#endif /*B_H_*/

//#include "B.h" end

class A
{
    private:
            B b;

    public:
            A() : b(*this) {}
};

#endif /*A_H_*/
//#include "A.h" end

int main()
{
    A a;
}

Now read the code. B is the first class the compiler encounters, and it includes an A& member. What is A? The compiler hasn't encountered any definition of A yet, so it issues an error.

The solution is to make a forward declaration of A. At some point before the definition of B, add a line class A;

This gives the compiler the necessary information, that A is a class. We don't know anything else about it yet, but since B only needs to include a reference to it, this is good enough. In the definition of A, we need a member of type B (not a reference), so here the entire definition of B has to be visible. Which it is, luckily.

jalf
Yes! This is what I realized soon after I posted my question. Thank you very much for your detailed response.
Scott
A: 

Some compilers (inc. gcc) also support #pragma once however the 'include guards' idiom in your question is the usual practice.

frankodwyer
A: 

In such situations, i create a common header to be included in all sources with forward declarations:

#ifndef common_hpp
#define common_hpp

class A;
class B;

#endif

Then the individual class header files typically don't need any #includes to reference other classes, if all that's needed are pointers or references to those classes. Half the time though there are other goodies in those headers, but at least any problem with circular references are solved with common.hpp

DarenW