This question already has answers here:
Resolve build errors due to circular dependency amongst classes
(12 answers)
Closed 2 years ago.
So, I know that there are many questions on stack overflow out there that are trying to handle circular dependencies. But none of those could really answer my question, if it is possible to have two classes know each other, and, more importantly, access information from each other. So, basically, I have read that you could use forward decleration, but with forward decleration, I couldn't access any fields. Maybe I should also add that I am really new to C++.But enough talking, let's get to an example:
Let's say we have a class called Scene and a class called EntityBase, which are defined as following:
EntityBase.h
#pragma once
#include <string>
#include "Scene.h"
class EntityBase
{
public:
std::string entityId;
Scene scene;
EntityBase(std::string entityId);
/*
This method will be called when this object is added to the scene
*/
void onAddedToScene(Scene* scene);
/*
This method will be called when this object is removed from the scene
*/
void onRemovedFromScene(Scene* scene);
};
Scene.h
#pragma once
#include <vector>
#include <string>
#include "EntityBase.h"
class Scene
{
public:
std::vector<EntityBase> entities;
std::string name;
Scene(std::string name);
void addToScene(EntityBase& entityBase);
};
The question arises,
How can I print out the name of Scene(and use every method of Scene) while EntityBase can fully access Scene too?
So, I'd really appreciate if you'd tell how to do this, because I will probably need to access every field and/or method later.
Since the C++17 standard you don't need the full EntityBase class definition for the Scene class, only a forward declaration:
#pragma once
#include <vector>
#include <string>
class EntityBase; // Forward declaration
class Scene
{
public:
std::vector<EntityBase> entities;
std::string name;
Scene(std::string name);
void addToScene(EntityBase& entityBase);
};
You of course need to include EntityBase.h anywhere where the entities vector is used, most notable in the Scene.cpp source file.
Since EntityBase is using an actual instance of Scene you can't do the same with that class and the EntityBase.h header file. Here you must include the Scene.h header file.
If you build targeting an older C++ standard (C++14 or earlier) then you must have the full definition of the EntityBase class for the vector.
As a possible workaround either make entities a vector of pointers to EntityBase; Or make EntityBase::scene a pointer and use forward declaration in the EntityBase.h header file instead. Or a combination of both.
Related
I have the following code, there is an 'Face' class which is composed of an 'Eyebrow' class. The expression of the face can be changed to one of the allowed public enums, thus controlling how each of it's constituent should be changed. Each of the constituent classes should be responsible for responding to a changed expression which is why I want to pass that along to each constituent.
face.h:
#include "eyebrow.h"
class Face {
public:
enum Expression {NEUTRAL, JOY, ANGER};
void setExpression(Expression);
private:
Eyebrow left_eyebrow;
}
face.cpp:
#include "face.h"
#include "eyebrow.h"
void Face::setExpression(Expression e) {
left_eyebrow.setExpression(e);
}
eyebrow.h:
#include "face.h"
class Eyebrow {
public:
void setExpression(Face::Expression);
};
The code is being compiled with Cmake:
add_executable(Main main.cpp face.cpp eyebrow.cpp)
I am getting the following compiler error: ‘Face’ has not been declared in void setExpression(Face::Expression_e) in eyebrow.h.
I then forward declared Face (see below) and got the compiler error: ‘Face::Expression’ has not been declared in void setExpression(Face::Expression); in eyebrow.h
eyebrow.h (with forward declaration):
#include "face.h"
class Face;
class Eyebrow {
public:
void setExpression(Face::Expression);
};
What is the recommended way of solving this, should friends be used?
You have created a circular dependency:
face.h includes eyebrow.h and eyebrow.h includes face.h, so they're including each other. You must resolve the circular dependency.
You could remove the enum from Face and put it in its own header file, which both files could then include.
Alternatively, you could store a pointer to Eyebrow in Face instead of Eyebrow itself. Then you could remove the include of eyebrow.h in face.h and forward-declare Eyebrow instead. The pointer can then be initialized in the constructor of Face.
Apologies if you have seen this question before however it has yet to be answered, essentially in my code I have two structs, defined in separate headers and used globally throughout the project. I simply wish to use both structs (which again, are defined in two separate headers) in other cpp files than just the ones that the header file belongs to.
Here is some sample code which I have tested:
class1.h
#include "class2.h"
#include <vector>
#include <string>
struct trans1{
string name;
};
class class1 {
private:
vector <trans2> t2;
public:
class1();
};
class2.h
#include "class1.h"
#include <vector>
#include <string>
struct trans2{
string type;
};
class class2{
private:
vector <trans1> t1;
public:
class2();
};
errorlog:
In file included from class1.h:3:0,
from class1.cpp:1:
class2.h:21:13: error: 'trans1' was not declared in this scope
vector <trans1> t1;
^
class2.h:21:19: error: template argument 1 is invalid
vector <trans1> t1;
^
class2.h:21:19: error: template argument 2 is invalid
I understand that this is ridiculous code in a real world application however this is the simplest way I could demonstrate.
It is worth noting that if I simply comment out the declaration of vector t1 or t2 under 'private:' the code compiles without fail. It is just the fact I am using a second struct.
Any help anyone? Thanks.
Simply forward-declare the classes that will be used. Put all implementation code into a cpp file, not inline in the header.
Make the vector private. This way no file that includes the header can force code generation against an incomplete class.
you can try to forward declare trans1 in class2.h and trans2 in class1.h like this:
class2.h :
// includes
struct trans1;
// rest of your code
the same thing (but with trans2) in class1.h
Don't forget to add Include guards in your code!
edit: and yes, you need to change your vectors to store pointers, otherwise it won't link
You need to put the "trans" structs in their own header file(s) and include them in your class header files.
You could forward declare them, but this would require changing your vector to use pointers. (In that case I would recommend std::vector<std::unique_ptr<trans>>). This could be appropriate if your structs are big and complex.
The main advantage of the forward-declaration approach is to reduce compile times. However if the structs are really so simple as in your example, I wouldn't bother with the extra overhead of using pointers here.
If You were to do this in single .cpp file, the solution would be trivial:
struct trans1 { ... };
struct trans2 { ... };
class class1 { ... };
class class2 { .... };
Now you just need to rearrange the code to get this result in every translation unit. (the order of classes/structs in the file is important)
I have been trying to write my own state machine where each state is a seperate class derived from a state base class.
Whereever I include my state_t class file (#include "state_t.h") I want all the derived state class headers to be included as well so I don't have to include them all seperatly each time I need to use the state machine or create a new state.
since "state_t" is not defined until the end of state_t.h I can only include my state files at the end of the file state_t.h. I have never written code that does this before and it seems a little odd! I could add a top-level "statemachine.h" which collects all the files together, but it seems a waste.
My question is: is it correct/safe/ok to do this? any draw-backs/issues?
Note: at the moment my code is all test code and its written in Qt, but it should be a straight-up c++ question.
Here is my base class (state_t.h) - notice the #include's at the end:
#ifndef STATE_T_H
#define STATE_T_H
#include <QByteArray>
#include <QDebug>
class state_t
{
public:
state_t(QByteArray stateName);
virtual ~state_t();
virtual state_t * processState(int input) = 0;
QByteArray getState();
QByteArray name;
};
#include "teststate1.h"
#include "teststate2.h"
#endif // STATE_T_H
Here is a state derived class (teststate1.h):
#ifndef TESTSTATE1_H
#define TESTSTATE1_H
#include "state_t.h"
class testState1 : public state_t
{
public:
testState1();
state_t *processState(int input);
};
#endif // TESTSTATE1_H
Here is my main.cpp:
#include <QCoreApplication>
#include <QDebug>
#include "state_t.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
state_t *myState = new testState1();
myState = myState->processState(1);
myState = myState->processState(2);
myState = myState->processState(3);
myState = myState->processState(1);
return a.exec();
}
Note: the code all works perfectly, its really a question of "correctness".
Given your particular example:
It is a bad idea. You introduce a very tight coupling between base and derived types in the wrong direction. A base class should know nothing about its derived types. That is one of the things that allows it to be an effective base class. In your current scheme, every time you write a derived type, you would have to touch the header of the base, forcing a compile time dependency on all client code of the base. Besides that, you have a cyclic include dependency.
In general:
In non-pathological cases, it depends on the situation. Concerning header files, one could argue that it is good to know which headers are required by a file, in which case it makes sense for them to be at the top. However, if the includes are considered to be implementation details that would only be a distraction, they could be placed at the bottom. In my experience this applies specifically to the implementation of template code, and implementation of helper classes and inline functions in anonymous namespaces.
Personally, I'd much rather have all the includes at the top. You can use forward declarations in your other headers to get around the define order issue.
But that's just a style thing - "correctness" wise, there's no reason why you can't do that. You can validly include anything anywhere you like, it's just likely to cause confusion later!
According to me, this is just a convention, as everybody do that, a standard dev will not take a look at the end of the file if he need to add some, and this will be a mess with some includes at the top and some at the bottom.
I prefer to have my includes at the top otherwise it could be a bit confused. My suggestion for you is do not include teststate1.h and teststate2.h in test_t.h instead create state_all.h
#include "state_t.h"
#include "teststate1.h"
#include "teststate2.h"
And include state_all.h instead of state_t.h where you need it
I'm gonna go C++ über n00b on this one and ask how is the best way to deal with a circular dependency when you have inheritance.
The set is simple: Scene class extends Actor; Scene has a pointer to a vector of Actors; Actor has a pointer for (parent) Scene.
As for include files I got:
Scene.h:
#include <string>
#include <vector>
using namespace std;
#ifndef __Scene_h__
#define __Scene_h__
#include "Actor.h"
namespace myns
{
// class Actor;
class Scene;
}
namespace myns
{
class Scene: public myns::Actor
{
/* private class attributes... */
public:
/* public class attributes... */
std::vector<myns::Actor*> actors;
Scene(/* arguments */);
/* public class methods... */
};
}
#endif
Actor.h
#include <string>
#include <vector>
using namespace std;
#ifndef __Actor_h__
#define __Actor_h__
#include "Scene.h"
namespace myns
{
// class Scene;
class Actor;
}
namespace myns
{
class Actor
{
/* private class attributes... */
public:
/* public class attributes... */
myns::Scene* scene;
Actor();
Actor(/* arguments */);
/* public class methods... */
};
}
#endif
But this gives me alot of C2504 errors/base class undefined on Visual Studio 2010.
If I comment the Scene.h include on the Actor.h and uncomment the forward declaration of Scene on Actor.h it works, but then, in my app, if I want to include only the Actor.h on a particular piece of code, it will not work. How can I put this to work while maintaining the inclusion independence for Actor.h - including Actor.h without the need of previously manually including Scene.h?
What is wrong with my class definitions and how is the best way to deal with this circular dependency?
Shouldn't the #ifndef directives prevent this inclusion problem?
Thanks in advance.
but then, in my app, if I want to include only the Actor.h on a particular piece of code, it will not work
What you need to do is in the .cpp file where you need to use define the Actor class you must include both Actor.h and Scene.h. That way the forward declaration will be resolved and everything should work.
As an aside, you should move your #ifndef and #define right to the top of the file, before the includes. Also, having a using in a header file is bad practice because other files that include your header might not work properly. It should be ok to put it inside your namespace myns { ... } though.
Is a Scene really a type of Actor?
If it is, Actors probably shouldn't know about Scenes. Base classes shouldn't usually know about their derived classes.
Where is the Liskov Substitution Principle here? What action do you perform on an Actor that would be polymorphically performed differently by a Scene.
In any case, Scene derives from Actor so must include its base class. But in Actor.h if you really do need the Scene class it must be a forward declaration only.
In the compilation units (the .cpp files) you might include both headers if required.
this is my first question here.
Writing some code, i receive this error from g++: "Entity was not declared in this scope", in this context:
#ifndef Psyco2D_GameManager_
#define Psyco2D_GameManager_
#include <vector>
#include "Entity.h"
namespace Psyco2D{
class GameManager{J
private:
std::vector<Entity> entities;
};
}
#endif
This is the content of Entity.h:
#ifndef Psyco2D_Entity_
#define Psyco2D_Entity_
#include <string>
#include "GameManager.h"
#include "EntityComponent.h"
namespace Psyco2D{
class Entity{
friend class GameManager;
private:
/* Identificatore */
std::string _name;
/* Components list */
std::map<const std::string, EntityComponent*> components;
protected:
Entity(const std::string name);
public:
inline const std::string getName() const{
return this->_name;
}
void addComponent(EntityComponent* component, const std::string name);
EntityComponent* lookupComponent(const std::string name) const;
void deleteComponent(const std::string name);
};
}
#endif
If i use std::vector<class Entity> instead of std::vector<Entity> it works.
Why?
Thanks to all =)
The problem is you have a cyclic dependency. Take out #include "GameManager.h" in Entity.h, since you don't actually need it in that header. (Up-vote this answer, which first pointed it out.)
Note the guards are actually the problem; but don't take them out! You just need to minimize the includes you have, and declare (and not define) types when possible. Consider what happens when you include Entity.h: As some point it includes GameManager.h, which in turn includes Entity.h. At this point, Entity.h already has its header guard defined, so it skips the contents. Then the parsing of GameManager.h continues, where upon it runs into Entity, and rightfully complains it is not defined. (Indeed, this is still the process of including GameManager.h in the first inclusion of Entity.h, far before Entity is defined!)
Note your numerous edits demonstrate why it's important to post real code, not re-synthesized code. You need real details to get real answers.
Old:
Entity is in the Psyco2D namespace. You need to specify that:
class GameManager{
private:
std::vector<Psyco2D::Entity> entities;
};
Assuming the first snippet is part of GameManager.h you have a circular header dependency. I believe you can fix this by changing the GameManager.h include in Entity.h to class GameManager; instead.
Additionally as GMan noted, Entity is in a namespace and you need to qualify Entity with the namespace name.
Remove the Psyco2D-namespace and it will work without declaring "class Entity".