How do I define C ++ classes with circular dependencies?
I am trying to create a basic object / object management system and I have two classes, one for the base class for the entities to inherit from, and one for managing and managing them.
This is the original code I am trying to use:
#include <iostream>
#define MAX_ENTS 400
class EFentity;
class EFiterator;
class EFentity {
public:
EFentity();
virtual bool step();
virtual void create(EFiterator*,int);
virtual void destroy();
private:
int holder_id;
EFiterator* holder;
};
EFentity::EFentity(void) {
// add base entity stuff
}
void EFentity::destroy() {
holder->ents[holder_id]=NULL;
std::cout << "destroying object id "<<holder_id;
delete this;
}
void EFentity::create(EFiterator* h,int pos) {
holder=h;
holder_id=pos;
}
bool EFentity::step() {
return false;
}
class EFiterator {
public:
EFentity* ents[MAX_ENTS];
int e_size;
EFiterator();
void push(EFentity* e);
void update();
};
EFiterator::EFiterator() {
e_size=0;
}
void EFiterator::update() {
for(int i=0;i<e_size;i++) {
if (!ents[i]->step()) {
std::cout << "entity id "<< i<<" generated a fault!\n";
} else std::cout << "entity id "<<i<<" passed step test.\n";
}
}
void EFiterator::push(EFentity* e) {
ents[e_size]=e;
e->create(this,e_size++);
}
int main() {
EFiterator main_iterator;
main_iterator.push(new EFentity());
main_iterator.update();
std::cin.get();
return 0;
}
This code obviously doesn't compile and here are the errors:
In member function `virtual void EFentity::destroy()':
[20] invalid use of undefined type `struct EFiterator'
[5] forward declaration of `struct EFiterator'
I've seen problems like this on SO before, but they didn't require access to member variables and functions of another class, so it could be easily solved with a pointer.
I think it can be solved by having a prototyped function to access the array internally EFiterator
, but is there a way to do this smoothly with some tricky class manipulation?
source to share
EFentity::destroy()
needs to know the specific type EFiterator
when it calls
holder->ents[holder_id]=NULL;
Put EFentity::destroy()
after EFiterator
definition should solve the problem, for example, put it afterEFiterator
void EFiterator::push(EFentity* e) {
ents[e_size]=e;
e->create(this,e_size++);
}
void EFentity::destroy() {
holder->ents[holder_id]=NULL;
std::cout << "destroying object id "<<holder_id;
delete this;
}
Usually forward ad types in a header file, and include the specific type in the file .cpp
that breaks down the issue circular include
.
EFentity.h
class EFiterator;
class EFentity {
public:
EFentity();
virtual bool step();
virtual void create(EFiterator*,int);
virtual void destroy();
private:
int holder_id;
EFiterator* holder;
};
EFentity.cpp
#include "EFiterator.h"
EFentity::EFentity(void) {
// add base entity stuff
}
void EFentity::destroy() {
holder->ents[holder_id]=NULL;
std::cout << "destroying object id "<<holder_id;
delete this;
}
void EFentity::create(EFiterator* h,int pos) {
holder=h;
holder_id=pos;
}
bool EFentity::step() {
return false;
}
source to share
The problem is that you have a forward-declared type EFIterator
and then tried to access its members before the definition is visible to the compiler. When the compiler reads the definition for EFentity::destroy()
, it needs to know what the EFIterator
named member has ents
.
The solution is easy to achieve: place two declarations in front of the definitions, for example:
#include <iostream>
#define MAX_ENTS 400
class EFentity;
// Now we can refer to EFentity by pointer or reference.
class EFiterator {
public:
EFentity* ents[MAX_ENTS];
int e_size;
EFiterator();
void push(EFentity* e);
void update();
};
// Now we can refer to EFiterator by pointer, reference, or value.
class EFentity {
public:
EFentity();
virtual bool step();
virtual void create(EFiterator*,int);
virtual void destroy();
private:
int holder_id;
EFiterator* holder;
};
// Now we can refer to EFentity by pointer, reference, or value.
EFiterator::EFiterator() {
// ...
Now the declaration for EFIterator
can refer to the type EFentity
as a pointer (it does not need to know anything else other than that it is a UDT), and the declaration for for EFentity
can refer to the type EFIterator
as a pointer (it also does not need to know anything other than this EFIterator
is a UDT). Ordering of the ads EFIterator
, and EFentity
it does not matter; you can just as easily invert them:
#include <iostream>
#define MAX_ENTS 400
class EFiterator;
// Now we can refer to EFiterator by pointer or reference.
class EFentity {
public:
EFentity();
virtual bool step();
virtual void create(EFiterator*, int);
virtual void destroy();
private:
int holder_id;
EFiterator* holder;
};
// Now we can refer to EFentity by pointer, reference, or value.
class EFiterator {
public:
EFentity* ents[MAX_ENTS];
int e_size;
EFiterator();
void push(EFentity*);
void update();
};
// Now we can refer to EFiterator by pointer, reference, or value.
EFiterator::EFiterator() {
// ...
Until you get to the definitions, you need to have previous declarations.
source to share