views:

1648

answers:

6

I have a problem which I think is related to forward declarations, but perhaps not.

Here is the relevant code:

A.h

#ifndef A_H_
#define A_H_

#include "B.h"

class A
{
    private:
     B b;

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

     void bar() {}
};

#endif /*A_H_*/

B.h

#ifndef B_H_
#define B_H_

#include "A.h"

class A;

class B
{
    private:
     A& a;

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

     void foo() { /*a.bar();*/ } //doesn't compile
};

#endif /*B_H_*/

main.cpp

#include "A.h"

int main()
{
    A a;

    return 0;
}

The problem seems to be with the invocation of A::bar(). The program successfully compiles until I attempt to call this method at which point I get two errors:

error: invalid use of incomplete type ‘struct A’

error: forward declaration of ‘struct A’

I presume this is because A::bar() has yet to be defined or declared since both headers reference each other. However, I forward declared class A and am at a loss as to what else I need to do. I am new to C++, so please forgive me. I could not find the answer to this question anywhere else online. As always, thanks in advance!

+7  A: 

You've got a circular reference, so you need to separate B.h. Try something like:

B.h:

#ifndef B_H_
#define B_H_

// don't include A.h here!

class A;

class B
{
   private:
      A& a;

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

      void foo();
};

#endif /*B_H_*/

B.cpp:

#include "B.h"
#include "A.h"

void B::foo() { a.bar(); } // now you're ok

Edit: explanation for why you need to split it into two files:

The class B contains a reference to A, which can be a so-called incomplete type. You can't call any functions on it because the compiler doesn't yet know what the heck A is - it just knows that it's a class of some sort. Once you include A.h (in the .cpp file), then A is a complete type, and you can do whatever you like with it.

You can't keep the whole thing in one header file because you'll get a circular reference. You're preventing an infinite loop with your include guards, but you're getting something you don't want. Look at what the compiler ends up with when you compile main.cpp, as you had it before:

// #include "A.h" ==>
#define A_H_

// #include "B.h" ==>
#define B_H_

// #include "A.h" ==> nothing happens! (since A_H_ is already defined)

class A;

class B {
private:
    A& a;

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

    void foo() { a.bar(); } // <-- what the heck is A here?
                            //     it's not defined until below
};

class A {
private:
   B b;

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

   void bar() {}
};

int main() {
    A a;
    return 0;
}
Jesse Beder
This would have the averse affect that B::foo() will no longer be an inline function
shoosh
I tried this previously, however, I neglected to move the "#include 'A.h'" into the B.cpp file! Thank you! However, is there any way to do this without splitting the class into two files?
Scott
So you could declare it inline if you want.
Jesse Beder
Shy: Perhaps I can declare the class and methods and then inline them below in the same file.
Scott
To be able to declare it inline the implementation of B::foo also needs to be in the h-file.
dalle
However, is it not confusing to attempt to inline every method? My understanding was that "inline" meant that the code would be inserted at the place where the method was called (which cannot always be done). In this case, it seems to have a different meaning. Can someone explain?
Scott
I only knew this could be done because I have seen it before in someone else's code.
Scott
Thanks for the extended explanation Jesse. I had the compiler's picture in my head having asked this question before.However, nobody explained the difference between complete and incomplete types.My intention to put the entire class in the .h file was not questioned since I did not call any methods.
Scott
A: 

In B.h, you are including A.h as well as forward declaring A.

You need to separate B.h into B.h and B.cpp, or remove the forward declaration.

PS You also have a circular dependency. A.h is including B.h, and vice versa. Your guards are catching the problem, though ;)

metao
A: 

To add to the other answer (circular reference, which is the correct answer), if you're coming from C#/Java, understand that C++ is different, in that files are parsed in order (instead of considered as a whole). So you need to be careful to ensure that everything is defined before it is used, in the actual order that files are included (and/or separate functionality into .cpp files as appropriate).

Nick
Files aren't parsed "in order"; my build environment actually shows them being parsed in parallel. Besides, you're confusing the sequential compiling within a file with the linking across files.
MSalters
+1  A: 

The line #include<file.h> will just replace the line with the content of file.h. So when the computer tries to compile your main.cpp, it will pull everything together, which looks like the following. At the place you want to use A::bar(), it has not been defined.

// result from #include "A.h"

#ifndef A_H_
#define A_H_

// Now, #include "B.h" in A.h will get you the following
#ifndef B_H_
#define B_H_

// here you include "A.h" again, but now it has no effect
// since A_H_ is already defined

class A;

class B
{
    private:
            A& a;
    public:
            B(A& a) : a(a) {}
            // Oops, you want to use a.bar() but it is not defined yet
            void foo() { /*a.bar();*/ } 
};

#endif /*B_H_*/

class A
{
    private:
            B b;
    public:
            A() : b(*this) {}
            void bar() {}
};
#endif /*A_H_*/

// now this is your main function
int main()
{
    A a;
    return 0;
}
PolyThinker
+1  A: 

As several others have mentioned, the circular reference appears to be your problem. Another phrase for this would be "mutual dependency". However, rather than attempting to find the proper syntax to get your application to compile and run (I assume the actual problem exists in a program slightly more advanced than what you have posted), I would encourage you to attack the problem from an object-oriented design standpoint.

As a general rule, mutual dependencies should be avoided wherever possible. I've come across this issue in my own code before (which resulted in several days of debugging, ripping my hair out, and cursing my compiler), and here is how I was finally able to overcome it. I will present a watered down version of my own problem as a concrete example of how to address the issue, so hopefully you can extract out the hidden meaning behind it all, and it will all make sense in the end.

Let's say we have two classes: Data and DataAnalyzer

Data holds a reference to DataAnalyzer (used to analyze the data), and DataAnalyzer holds a reference to Data (the data to be analyzed) -- mutual dependency! In order to eliminate this dependency, we extract out an interface (in C++, a pure virtual class) from DataAnalyzer that defines the public methods/attributes required on DataAnalyzer. It might look something like:

class IAnalyzer
{
public:
    virtual void Analyze () = 0;
};

When we define DataAnalyzer, we do it as follows:

class DataAnalyzer : public IAnalyzer
{
public:
    DataAnalyzer (Data* data);

    virtual void Analyze (); // defined in DataAnalyzer.cpp
};

And Data looks like:

class Data
{
public:
    Data ();

    IAnalyzer* Analyzer;
};

Somewhere, in your controller class, you might have something like:

void main ()
{
    Data*  data = new Data ();
    data->Analyzer = new DataAnalyzer (data);
}

Now, Data stands by itself (as far as it knows, IAnalyzer does not require a reference to Data), and only DataAnalyzer is dependent on Data. If you want to keep going, you could continue to remove DataAnalyzer's dependency on Data, but for the sake of simply breaking the mutual dependency, this should be sufficient.

Warning: I have not compile tested this code, so it may require some minor adjustment to compile and run properly.

Good luck!

ph0enix
A: 

If you really want B::foo to be inline you could do the implementation in B.h, although I would not recommend it.

B.h:

#ifndef B_H_
#define B_H_

// Forward declaration of A.
class A;

class B
{
private:
    A& a;

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

    void foo();
};

// Include definition of A after definition of B.
#include "A.h"

inline void B::foo()
{
    a.bar();
}

#endif /*B_H_*/

A.h:

// Include definition of B before definition of A.
// This must be done before the ifndef A_H_ include sentry,
// otherwise A.h cannot be included without including A.h first.
#include "B.h"

#ifndef A_H_
#define A_H_

class A
{
private:
    B b;

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

    void bar() {}
};

#endif /*A_H_*/
dalle