classes referencing each other

hi,

I am transitioning from python to cpp, and I was learning some of the design patterns. The code bellow is from https://refactoring.guru/design-patterns/state/cpp/example

My question is if there is any specifiq workflow when you have two classes referencin each other. In the example, you can see that it is requiered apparently to do an empty class declaration on "Context", since "State" refences it right after in 'void set_context(Context *context)"'. Then when you define the class "Context", "State" is refence too as private memenber.
So it isn't ideal to wright code like that because you are forced to follow a specific sequence, and any refactoring can potentially break the code.
Does c++ has any mechanisms for this?

thanks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#include <typeinfo>


class Context;  // first, declaration to be able to use in State


class State {
 protected:
  Context *context_;

 public:
  virtual ~State() {
  }

  void set_context(Context *context) {
    this->context_ = context;
  }

  virtual void Handle1() = 0;
  virtual void Handle2() = 0;
};



class Context {  // Actual definition

 private:
  State *state_;

 public:
  Context(State *state) : state_(nullptr) {
    this->TransitionTo(state);
  }
  // ... more code 
}
It's quite common in C++ to separate the code into header files (.h) and implementation files (.cpp).

The header file (.h) only contains class definition and function declarations.
The implementation file (.cpp) contains function definitions.

State.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Context;

class State {
 protected:
  Context *context_;

 public:
  virtual ~State();

  void set_context(Context *context);

  virtual void Handle1() = 0;
  virtual void Handle2() = 0;
};

State.cpp
1
2
3
4
5
6
7
8
#include "State.h"

State::~State() {
}

void State::set_context(Context *context) {
  this->context_ = context;
}

Context.h
1
2
3
4
5
6
7
8
9
10
11
class State;

class Context {

 private:
  State *state_;

 public:
  Context(State *state);
  void TransitionTo(State *state);
};

Context.cpp
1
2
3
4
5
6
7
8
9
#include "Context.h"

Context::Context(State *state) : state_(nullptr) {
  this->TransitionTo(state);
}

void Context::TransitionTo(State *state) {
  ...
}

EDIT: Got the includes mixed up. Now they're correct.

As long as you use forward declarations whenever you can and only include headers in the files that need the full class definition then these sort of circular dependencies are unlikely to cause any problems.

Note that in your real code you might have to include State.h from Context.cpp and Context.h from State.cpp. That's not going to cause any problems.

In some situations you might also need to include one of the headers from the other header but that is not an issue if only one of the headers does it. But if both headers tried to include each other that would not work.
Last edited on
The above is how I have solved problems in the past and it has worked pretty well.

But I'm not sure it's necessarily the "best" way to do it. It's quite nice to avoid circular dependencies if you can so if you could write your code so that the dependency only went in one direction (e.g. Context knows about State but State knows nothing about the Context) that might be a better approach.

C++20 added something called "modules" which is intended to be a modern replacement for .h/.cpp files (kind of). Compiler and tool support for this feature is still a bit lacking so I haven't tested it yet but my understanding is that you're not going to be able to forward declare types from other modules so I'm thinking that maybe I will have to force myself to write my code to avoid these kind of circular dependences all together (which might be a good thing, I don't know yet) ... or maybe I'll need to put multiple classes inside the same module?
Last edited on
cool,
Sure, I use headers and source files in all my projects. But was getting a circular dependency in this example. The solution is as you posted. Simply declare class context at the beggining of state.h. Thanks.

About circular dependencies sometime they are actually part of the pattern itself.
State, Visitor, and other pattern requiere a ciruclar dependency because referencing each other is part of the design. But I agree as a general rule it is better to avoid them.

cheers,
R

Topic archived. No new replies allowed.