Reputation: 312
I'm trying to set up a basic entity/object management system, and I have two classes, one for being a base-class for entities from which to inherit, and one to manage and control them all.
This is the source code I'm 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 have seen problems like this on SO before, but they did not require access to member variables and functions of the other class, so it could be easily solved with a pointer.
I think this can be solved by having a prototyped function to access the array inside of EFiterator
, but is there a way to do this smoothly with some tricky class manipulation?
Upvotes: 3
Views: 261
Reputation: 15269
The problem is that you've 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 that EFIterator
has a member called ents
.
The solution is easy to reach: Put the two declarations ahead of the definitions, like so:
#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 reference type EFentity
as a pointer (not needing to know anything else other than that it's a UDT), and the declaration for EFentity
can refer to type EFiterator
as a pointer (also not needing to know anything other than that EFiterator
is a UDT). The order of the EFiterator
and EFentity
declarations doesn't matter; you could just as easily invert them like this:
#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() {
// ...
Not until you get to the definitions do you need to have the preceding declarations available.
Upvotes: 1
Reputation: 45450
EFentity::destroy()
needs know concrete type of EFiterator
when it calls
holder->ents[holder_id]=NULL;
Put EFentity::destroy()
after EFiterator
definition should resolve the problem, for example, put it after EFiterator
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;
}
Normally forward declare types in header file and include concrete type in .cpp
file, which breaks circular include
issue.
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;
}
Upvotes: 3