How can I avoid circular dependencies between Entities and Map?

Viewed 143

Lets say there are two classes: Map and Entity. Map has an array of pointers to all Entities in the game, and the entity uses a map to check if there is an empty spot where he is going to go. So, in the main function, I am doing the following:

map->getEntity(x, y)->walk(map)

which causes a circular dependency between the entity and the map. I tried moving the walking logic of an entity to the Map class, but it caused many other problems, and it was not quite right in my opinion, because i wanted to encapsulate all the logic inside an entity.walk() method (checking whether target field is reachable by the the entity, whether is empty, etc), so i dont need to check all of this in my main function before i move an entity. I also tried using forward declaration, but it can't help in this situation, because both map and entity are using each other's methods, which are not declared yet. Here's what I have now:

class Entity;
class Map {
public:
    void add(Entity* ent) {
        ent->getX()                       //Map.h
        //...
    }
}
#include "Map.h"
class Entity {
     void walk(Map* map) {
         if(map->isEmpty(x, y)) {
             //...                        //Entity.h
         }
     }
}
2 Answers

Just have a method declaration in the header file, and nothing more. Simply do not define the methods inline:

class Entity;

class Map {
public:
    void add(Entity* ent);
}

Then in your map.cpp file you include both headers, and define the method:

void Map::add(Entity* ent) {
    ent->getX()
}

map.cpp #includes both headers files, first, and this becomes a big nothing-burger. Use a similar solution, with both Map's and Entity's methods, to resolve their circular dependencies.

The answer is: Separate method definitions and implementations into separate file(s).

The pointer/reference can be used with forward declaration of Entity/Map, but when you are using map->isEmpty or ent->getX the needs to know whole class definition already.

In your short example this should be enough

class Entity;
class Map {
public:
    void add(Entity* ent);
    bool isEmpty(size_t x, size_t y) { return ...; } // whatever is it
}

class Entity {
     void walk(Map* map) {
         if(map->isEmpty(x, y)) {
             //...                 
         }
     }
     size_t getX() { return ...; } // whatever
}

void Map::add(Entity* ent) {
    ent->getX()                    
    //...
}
Related