7

I have a problem creating some form of hierarchy with different object types. I have a class which has a member of another class, like this:

class A
{
public:
    A(){}
    ~A(){}

    void addB(B* dep){
        child = dep;
        dep->addOwner(this);
    }
    void updateChild(){
        child->printOwner();
    }
    void print(){
        printf("Printing...");
    }
private:
    B* child;
};

And this is class B:

class B
{
public:
    void addOwner(A* owner){
        ownerObject = owner;
    }

    //ISNT WORKING
    void printOwner(){
        ownerObject->print();
    }

private:
    A* ownerObject;
};

Calling a function of "B" out of class "A" works just fine but trying it vice versa gives a compiler error because A is not defined in B. It actually is by using an include and a forward declaration, but I guess its a cross reference problem which the compiler can not solve.

Is there any chance to solve this problem or should I rethink my design?

SideEffect
  • 401
  • 3
  • 5
  • 10

4 Answers4

12

You say that you already solved your circular dependency problem by using a forward declaration of A instead of including the header where A is defined, so you already know how to avoid circular includes. However, you should be aware of what is possible and what is not with incomplete types (i.e. types that have been forward declared).

In your case, you try to call the member function print on an object that has an incomplete type; the compiler knows nothing about this type excepts that it will be defined at some point, so it does not allow you to do this. The solution is to remove the implementation of the printOwner member function from the B header and put it into an implementation file:

//B.hpp

class A; // forward declaration

class B
{
  public:
    void addOwner(A* owner);

    void printOwner() const; // I think this member function could be const

  private:
    A* ownerObject;
};

//B.cpp

#include "B.hpp"
#include "A.hpp" // here we "import" the definition of A

void B::addOwner(A * owner)
{
    ownerObject = owner;
}

void B::printOwner() const
{
    ownerObject->print(); //A is complete now, so we can use its member functions
}

You could possibly do the same thing in the A header.

Community
  • 1
  • 1
Luc Touraille
  • 79,925
  • 15
  • 92
  • 137
3

You can use forward declaration, and define the member functions outside of the class, i.e.

// A.h
class B;
class A { public:
  void addB(B* dep);   // don't define addB here.
  ...
};

// B.h
class A;
class B { public:
  void addOwner(A* owner);  // don't define addOwner here.
  ...
};

// A.cpp
#include "A.h"
#include "B.h"
void A::addB(B* dep) { 
   ...
}

// B.cpp
// similar.
kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
2

You probably should rethink your design, since a crcular parent-child relationship is usually a code smell.

But, you can make the compiler happy :

#include <cstdlib>
#include <cstdio>

class A
{
public:
    A(){}
    ~A(){}

    void addB(class B* dep);
    void updateChild();
    void print(){
        printf("Printing...");
    }
private:
    class B* child;
};

class B
{
public:
    void addOwner(A* owner){
        ownerObject = owner;
    }

    //ISNT WORKING
    void printOwner(){
        ownerObject->print();
    }

private:
    A* ownerObject;
};

void A::addB(class B* dep){
    child = dep;
    dep->addOwner(this);
}

void A::updateChild(){
    child->printOwner();
}



int main()
{
    return 0;
}
John Dibling
  • 99,718
  • 31
  • 186
  • 324
  • Thank you! The real idea behind this "design" was to have a class (in this example A) which works as a container for a lot of abstract classes ("B"), so that all the "B"s extending the functionality of "A" based on their concrete implementation. For the functionality which is the same for all B-objects I'm creating a implementation in A, because it would not make sense to have 10 B-objects having identical functionality. At least it would be a synchronisation problem as well. – SideEffect Sep 17 '10 at 13:24
  • @user450556: Sounds to me like you might want to look in to policy-based design – John Dibling Sep 17 '10 at 18:38
0

You should move B::printOwner implementation to .cpp file.

Abyx
  • 12,345
  • 5
  • 44
  • 76