Reputation: 77
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
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