Circular References in Header Files

Sorry for posting a general c++ question here but this forum seems to have become my primary source for programming help. :smiley: I have 2 classes that need to store pointers of each other. I tried to simply include the other class in each of the headers but that doesnā€™t seem to work. I Then tried forward declarations and they did work. My question is when should I include headers and when should I use forward declarationsā€¦ Should I use forward declarations in the header files and includes in the cpp file? Also, the compiler reports the same errors multiple times for header files despite using the #ifndef #define #endif multiple inclusion protection.

Thanks,
Caleb

If youā€™re defining a class, but only declaring its member functions, then you can easily add a pointer to a different class. You donā€™t need to know the structure of a class to be able to hold a pointer to it - itā€™s only when you try to dereference it, or use it to call a member function of that class, that you find a problem.

So, the solution is always to separate the implementation of your functions (specifically any that make use of the pointer) from the class definition.

you obviously know your stuff and so i apologise if the rest of this sounds really elementary and childish! but examples are fun!

For example, in my class I know that i need to make use of a gwargleā€¦ i even know that a particular function will take/return a gwargleā€¦ but right now i donā€™t know what a gwargle is, and thatā€™s okay because i donā€™t yet know what those functions will try to do with it. As long as i know that a gwargle exists (forward declaration) and i donā€™t try to do anything with it, iā€™m okay. The same goes for the gwargleā€¦ he is expecting to have to do something with me at some pointā€¦ he knows heā€™s got to bear me in mind, but until he goes off to compile his plans, he doesnā€™t need to know what i actually am. And by the time heā€™s got back, heā€™ll have been given a blueprint showing exactly what he needs to know about me. Heck, iā€™ve just started learning my ā€˜gwargleā€™ functions and iā€™ve got here a gwargle schematic so i know which bits do what.

So a ā€˜simpleā€™ solution is to make sure that both classes have their classes defined each in a separate header, each keeping their member function definitions in an external implementation file. The class definitions in the headers are each preceded with a forward declaration of the other, and both implementation files include both headers.

Hereā€™s another simplistic example. [Donā€™t worry about reading all the code, the important bits are commented and obviousā€¦]

Most basic example, with obvious errors:


class A
{
public:
   int a;
   B* b_ptr;     // <--- first error

   void f()
   {
      b_ptr->b = a;
   }
};

class B
{
public:
   int b;
   A* a_ptr;
   
   void f()
   {
      a_ptr->a = b;
   }
};

Here, class A gives an error, trying to make a pointer to a ā€˜Bā€™ (which the compiler does not yet know about)ā€¦ a forward declaration of class B enables the compiler to know enough to allow a pointer to existā€¦


class B; // forward declaration...

class A
{
public:
   int a;
   B* b_ptr;  // <--- this is okay now...

   void f()
   {
      b_ptr->b = a;  // <--- but THIS is not okay
   }
};

class B
{
public:
   int b;
   A* a_ptr;
   
   void f()
   {
      a_ptr->a = b;
   }
};

ā€¦ but now the key problem is that the compiler still doesnā€™t know how to use the pointer- it certainly doesnā€™t know that there is a ā€˜bā€™ int member in this ā€˜Bā€™ class type.

So we take the function definition outside, away from the class definition, leaving just a declarationā€¦


class B; // forward declaration...

class A
{
public:
   int a;
   B* b_ptr;  

   void f();   // <-- the definition would be in A.cpp
};

class B
{
public:
   int b;
   A* a_ptr;
   
   void f()
   {
      a_ptr->a = b;
   }
};

Everything else in this example is now fine. Of course, we can take it further by putting B into its own file.



// A.h
//----------------------
class B; // forward declaration...

class A
{
public:
   int a;
   B* b_ptr;  

   void f();   // <-- the definition would be in A.cpp
};


// B.h
//----------------------
class A; // forward declaration...

class B
{
public:
   int b;
   A* a_ptr;  

   void f();   // <-- the definition would be in B.cpp
};
// A.cpp
//----------------------



void A::f()
{
   b_ptr->b = a;   
}
// B.cpp
//----------------------



void B::f()
{
   a_ptr->a = b;   
}

sorry that was such a ā€œ21 days!ā€ answer :hihi: and sorry if itā€™s talking about way more obvious things than you asked for!

i think i answered your question somewhere in there tho! thatā€™s what iā€™d do anyway.

:lol: yup itā€™s in there, thanx! better more information than not enough.

The amount of times Iā€™ve done a bloody great class all inline stylee then fucking decided itā€™s goning to need a circular! Grrrrr!!

Whereā€™s the ā€œre-implement inline functions using impl fileā€ button anyway?

yeah itā€™s a pain in the bā€™txz0rsz

i use MSVC++Express, and when i find that i need to strip out the implementation to a .cpp file, itā€™s not usually too troublesomeā€¦

  • copy all the inlined functions from the .h to the new empty .cpp

  • in the .h, set outlining to ā€˜collapse to definitionsā€™, so that the function definitions show up as little [ā€¦] blocks after the headers.

  • put the cursor on the first function header, and then use the following key sequenceā€¦
    [END] [BACKSPACE] [ ; ] [DOWNARROW]
    ā€¦ that will move to the end of the row, delete the whole function block, put a statement closing semicolon on the end, and move to the next line. keep doing that (pressing DOWN several times if there are gaps between the rows) and your functions have been stripped down.

  • in the .cpp file, you just need to fill your clipboard with a ā€œClassName::ā€ to paste in front of all the function names. i usually just copy the constructor name, paste it infront of itself and put in the ::, then copy that whole first bit. Then itā€™s just a case of using the mouse and CTRL-V ā€¦ click just before the function name, ctrl-v, etcā€¦

  • then, all thatā€™s left is to add in the .cpp file is the relevant #include to get the class interface, and also delete any default value assignments in your function headers, as they only need to show in the header file.

of course everyone knows how to do that, but there are my ā€˜speedyā€™ tips on how to try to not kill yourself when you find yourself in that situation.