Yami OG
Yami OG

Reputation: 77

How to Foward Declare Classes Across Files C++

I have been trying to figure out how to forward declare classes across multiple files in C++. I was able to successfully do it with two classes, but doing this across many classes puzzled me. I tried forward declaring all the classes in a header file and including that in all the classes, but I got the error of incomplete classes. I looked into it, but all the solutions seemed unreasonable or confused me. Can you please explain a good way to do it.

If it helps here is some example code of the issue that specifically occurring. Class A is included in Class C, but Class A also includes Class B which holds Class C resulting in an error because Class C has not been declared.

Class Forward Declaration

//Tried "Class Name {};" but that also failed
Class A;
Class B;
Class C;

Class A

//Class A
#include "B.h"

class A{
 private:
  B b;
 public:
  void SetBInt(int set) {b.SetInt(set);}
}

Class B

//Class B
#include "C.h"
class B{
 private:
  C c;
  int i = 0;
 public:
  void SetInt(int set){i = set;}
}

Class C

//Class C
#include "A.h"
class C{
 private:
  int i = 0;
 public:
  void SetInt(int set){i = set;}
}

Upvotes: 1

Views: 646

Answers (1)

Mark Storer
Mark Storer

Reputation: 15868

You have to avoid circular dependencies. One common way to avoid them is by pre-declaring the type, and not having any instances or function calls of that type in the header.

You do this by changing instances, such as B b, into references or pointers: B* b. You also have to move any code that uses b into the CPP file, so you can #include "B.h" prior to using it.

For example, instead of

// we'll remove this header's dependency on B shortly
#include "B.h"

class A
{
  B b;
public:
  A() { b.SetInt(0); }
};

You do something like this:

class B;

class A
{
  B *b; // *pointers and &references to a predeclared class are fine.
public:
  A();
  ~A();
};

... and then in A.cpp:

#include "A.h" // ALWAYS include your own header first
#include "B.h"

A::A()
{
  // we didn't need to know how big B was until now, when we're about to make one and
  // run its constructor.  We'll also need to know what functions are available, and the
  // types of all its parameters (or lack of parameters).
  b = new B();
  b->SetInt(0);
}

A::~A()
{
  delete b; // for every "new" there must be a "delete"
}

Yes, this would be (much) better with std::unique_ptr<B> b instead of a raw pointer. With a smart pointer we wouldn't need an explicit destructor. I just didn't want to throw too much potentially new stuff at someone still learning.

Alternatively, you could pass a reference into the constructor, and use the member initializer list (you can only set references when they're constructed). This raises problems with "Object Lifetime". If the instance of B you pass in at construction time is destructed before A is done with it, you're program will violently crash (if you're lucky: that way you'll have a good idea of what the problem is) the next time it's used. Best to avoid it till you have considerably more experience under your belt.

Upvotes: 2

Related Questions