Question

I am working on a little game engine but I got stuck at something. Explanation : I have two classes, cEntity And ObjectFactory :

cEntity

class cEntity:public cEntityProperty
{
    Vector2 position;
    Vector2 scale;
  public:
   cEntity(void);
   cEntity(const cEntity&);
   ~cEntity(void);
  public:
   void init();
   void render();
   void update();
   void release();
};

ObjectFactory

#include "cEntity.h"
#include <vector>

class ObjectFactory
{
   static std::vector<cEntity> *entityList;
   static int i, j;
  public:
   static void addEntity(cEntity entity) {
   entityList->push_back(entity);
}
  private:
   ObjectFactory(void);
   ~ObjectFactory(void);
};

std::vector<cEntity> *ObjectFactory::entityList = new std::vector<cEntity>();

Now I am adding new cEnity to ObjectFactory in cEntity constructor but facing an error related to circular references: for using ObjectFactor::addEntity() I need to define the ObjectFactory.h in cEntity class but it creates a circular reference.

Was it helpful?

Solution

I think your code might have an underlying architectural issue given how you have described the problem.

Your ObjectFactory should be handling the cEntities, which in turn should be unaware of the "level above". From the description of the problem you are having, it implies that you're not sure what class is in charge of what job.

Your cEntitys should expose an interface (i.e. all the stuff marked "public" in a class) that other bits of code interact with. Your ObjectFactory (which is a bit badly named if doing this job, but whatever) should in turn use that interface. The cEntitys shouldn't care who is using the interface: they have one job to do, and they do it. The ObjectFactory should have one job to do that requires it to keep a list of cEntitys around. You don't edit std::string when you use it elsewhere: why is your class any different?


That being said, there's two parts to resolving circular dependencies (beyond "Don't create code that has circular dependencies in the first place" - see the first part to this answer. That's the best way to avoid this sort of problem in my opinion)

1) Include guards. Do something like this to each header (.h) file:

#ifndef CENTITY_H
#define CENTITY_H
class cEntity:public cEntityProperty
{
Vector2 position;
Vector2 scale;
public:
cEntity(void);
cEntity(const cEntity&);
~cEntity(void);
public:
void init();
void render();
void update();
void release();
};
#endif

What this does:

  • The first time your file is included, CENTITY_H is not defined. The ifndef macro is thus true, and moves to the next line (defining CENTITY_H), before it moves onto the rest of your header.
  • The second time (and all future times), CENTITY_H is defined, so the ifndef macro skips straight to the endif, skipping your header. Subsequently, your header code only ever ends up in your compiled program once. If you want more details, try looking up how the Linker process.

2) Forward-declaration of your classes.

If ClassA needs a member of type ClassB, and ClassB needs a member of type ClassA you have a problem: neither class knows how much memory it needs to be allocated because it's dependant on another class containing itself.

The solution is that you have a pointer to the other class. Pointers are a fixed and known size by the compiler, so we don't have a problem. We do, however, need to tell the compiler to not worry too much if it runs into a symbol (class name) that we haven't previously defined yet, so we just add class Whatever; before we start using it.

In your case, change cEntity instances to pointers, and forward-declare the class at the start. You are now able to freely use ObjectFactory in cEntity.

#include "cEntity.h"
#include <vector>

class cEntity;  // Compiler knows that we'll totally define this later, if we haven't already

class ObjectFactory
{
static std::vector<cEntity*> *entityList;  // vector of pointers
static int i, j;
public:
static void addEntity(cEntity* entity) {  
entityList->push_back(entity);
}
// Equally valid would be:
// static void addEntity(cEntity entity) {
// entityList->push_back(&entity);}
// (in both cases, you're pushing an address onto the vector.)
// Function arguments don't matter when the class is trying to work out how big it is in memory
private:
ObjectFactory(void);
~ObjectFactory(void);
};
std::vector<cEntity*> *ObjectFactory::entityList = new std::vector<cEntity*>();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top