Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Forward Declaration Problem when calling Method

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!

like image 439
Scott Avatar asked Jan 20 '09 06:01

Scott


1 Answers

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;
}
like image 120
Jesse Beder Avatar answered Nov 18 '22 12:11

Jesse Beder