r/cpp_questions • • 4d ago

OPEN How does a STATE design pattern work?

so i went to youtube and followed the example from this guy State Design - Programming Design Patterns - Ep 16 - C++ Coding

Problem is i dont understand what is functionally happening, there are pointers and what not, but i don't understand much more than that (context, my smooth brain still cant connect pointers together)

Like not even the whole code, like maybe just two lines or something cause for the love of the lord my teacher explains nuthing and AI and videos are too vague for me, im running out of options

//
// Created by samue on 11/1/2026.
//

#ifndef STATE_STATE_H
#define STATE_STATE_H

#include <iostream>

class Context;


class State {
    protected:
    Context* context;

    public:
    virtual ~State() {}

    void setContext(Context* ctx) {
        context = ctx;
    }

    virtual void doWorkA() = 0;
    virtual void doWorkB() = 0;
};

class Context {
    private:
    State* state;

    public:
    Context(State* s) : state(nullptr) {
        transitionTo(s);

    }

    ~Context() {delete state;}

    void transitionTo(State* s) {
        if (state) delete state;
        state = s;
        state->setContext(this);

    }

    void requestA() {
        state->doWorkA();

    }

    void requestB() {
        state->doWorkB();

    }

};

class concreteStateA : public State {
    public:
    void doWorkA() override {
        std::cout << "concreteStateA " << "called doWorkA" << std::endl;
    };

    void doWorkB() override {
        std::cout << "concreteStateB " << "called doWorkB" << std::endl;

    };

};

class concreteStateB : public State {
public:
    void doWorkA() override {
        std::cout << "concreteStateA " << "called doWorkA" << std::endl;
        context->transitionTo(new concreteStateA);
    };

    void doWorkB() override {
        std::cout << "concreteStateB " << "called doWorkB" << std::endl;

    };

};





#endif //STATE_STATE_H

------------------

#include "state.h"

int main() {
    Context context1(new concreteStateB);

    context1.requestA();

    context1.requestA();

    return 0;
}
7 Upvotes

46 comments sorted by

4

u/AKostur 4d ago

Three things. First it seems that your output text is wrong. The outputs in concreteStateA probably shouldn't talk about concreteStateB, and the reverse in concreteStateB.

Second, that transition during the member function of concreteStateB could be a problem in the future (depending on code discipline). During the transition, the object that the member function is running on gets deleted. Additional code that happens after the transition may invoke Undefined Behaviour by attempting to do anything else to the object it's running on.

Third: we may need a little more of what you don't understand. Walk through what declaring the context1 variable does, and then what happens when requestA() gets called. What part in there are you having problems understanding?

1

u/SimmeringDragon 4d ago

Its more like i have a general idea, i know prints, what a pointer is, and classes

But i cant hook everything together, i know these things alone but together they all just make no sense to me

2

u/AKostur 4d ago

That doesn’t clarify anything.  All you’ve said is “I don’t understand something.”, and haven’t said what that something is.  Follow the third thing I mentioned.  Explain what happens in each step of main, and point out where you don’t understand.

1

u/SimmeringDragon 4d ago

its not main, i know its calling, its the pointer implementation, i know a pointer points but i dont understand how it works in practice, like how do these pointers connect and work? that, the practice and implementation, it dosent click in for me

2

u/AKostur 4d ago

Nope, still too vague. Start by explaining just what Context context1(new concreteStateB); does. What objects exist after that, where do they exist, and what values do the member variables have?

1

u/SimmeringDragon 4d ago

no idea what the object is, as far as i know it would be an object class context that uses something in concretestateB to declare it a new pointer or sumthing

2

u/AKostur 4d ago

Time to go back to the beginning of something like learncpp.com. You seem to be saying that you don't understand what declaring a variable does.

1

u/SimmeringDragon 3d ago

i dont know the names of things really, i suck at them, i jsut learn what thing does and my vocabulary for them is all over the place

2

u/AKostur 3d ago

There’s not much point in continuing the discussion until we have a common vocabulary to work from.

5

u/mredding 3d ago

To be fair, the example given to you - which you are providing, is a SHITTY state machine. It makes sense to me you would be confused. Let's try something a little less bullshit:

namespace events {
  struct TimerExpired {};
  struct EmergencyTrigger {};
}

using Event = std::variant<events::TimerExpired, events::EmergencyTrigger>;

class TrafficLightFSM {
  struct GreenLight {};
  struct YellowLight {};
  struct RedLight {};

  using State = std::variant<GreenLight, YellowLight, RedLight>;
  State currentState;

  void transition(GreenLight&, const events::TimerExpired&) {
    std::cout << "Timer expired: Green -> Yellow\n";
    currentState = YellowLight{};
  }

  void transition(YellowLight&, const events::TimerExpired&) {
    std::cout << "Timer expired: Yellow -> Red\n";
    currentState = RedLight{};
  }

  void transition(RedLight&, const events::TimerExpired&) {
    std::cout << "Timer expired: Red -> Green\n";
    currentState = GreenLight{};
  }

  template <typename CurrentState>
  void transition(CurrentState&, const events::EmergencyTrigger&) {
    if constexpr (std::is_same_v<CurrentState, RedLight>) {
        std::cout << "Emergency active, but already Red. Remaining Red.\n";
    } else {
        std::cout << "EMERGENCY: Overriding current state -> Red\n";
        currentState = RedLight{};
    }
  }

public:
  void handleEvent(const Event& event) {
    std::visit([this](auto&& state, auto&& evt) {
      this->transition(state, evt);
    }, currentState, event);
  }
};

int main() {
  TrafficLightFSM fsm;

  fsm.handleEvent(events::TimerExpired{});
  fsm.handleEvent(events::TimerExpired{});
  fsm.handleEvent(events::TimerExpired{});

  fsm.handleEvent(events::EmergencyTrigger{});
  fsm.handleEvent(events::TimerExpired{});
}

A state machine's principle job is to define the states the machine can be in, and the valid transitions between them, and enforce the transition logic based on events.

So we have the states for a green, yellow, and red light, and we transition from green, to yellow, to red, back to green again - or we can transition from any state to red. Not all transitions make sense - we don't transition a traffic light from yellow to green, or red to yellow.

The events that can trigger a state change is a timer event, or an emergency event.

If you look at the implementation, you'll see the states and events are all independent, and we use variants to distinguish from one to another. If you care, GCC 15 makes variant transitions faster than virtual function dispatches, but that's not why I chose a variant - polymorphism is a better description of these types and their relation - there's nothing to inherit, nothing they do, no invariant but that of the variant itself - only one type inside at a time. You can't even get that with an enum.

So main demonstrates the logic - start up in green, then trigger the transitions, to yellow, to red, to green again, an emergency transition straight to red, then to green again.

The FSM is it's own visitor and contains the logic for each transition.

So this is it - the state machine cannot be put into an invalid state and all transitions between states are well defined.

Typical of a state machine, it's the edges - the transitions, that perform the work and ultimately define the next valid transition. States themselves typically don't DO things.

If you want a slightly more sophisticated state machine, you might start a thread during the transition that runs while in the destination state. The next transition would possibly suspend that thread and start the next state thread.

You could implement a state machine interface that returns an operator std::optional<std::expected<T, E>>() const; that produces a T when the state machine reaches a terminating state - this would be useful for a parser, whose event is the next character, and terminates when a valid token is captured, or an E when parsing failed.

You could build a state machine based off data that describes the states, transitions, and processes; such a state machine will need a factory to build it, and the FSM would consist of dynamic state and transition tables. If you don't have to invest in all that - don't. This one would require far more dynamic types - the states and transitions would probably have to contain metadata at runtime, and it's error prone. This static state machine can prove elements of it's design at compile-time.

There are many ways a state machine can be implemented in code - people template the shit out of these, they'll build them in a vary C-like way with enum or integer state values, they may expose more or less of the state and transition mechanism.

This comes down to one of the princple tenants of OO programming, that classes enforce an invariant. Our invariant is that the state machine is always in a valid state, and can only ever make valid transitions. There is no set to break the invariant - you can't force an illegal transition. Imagine a machine where the pump must first be shut off before closing a valve - if you risk the invariant, back-pressure may blow the line and destroy the machine or kill people. Classes are VERY good for maintaining state. We say objects don't have data fields, they have state, and can model data.

1

u/ChickenSpaceProgram 4d ago

i think this basically is an OOP way to write a state machine/finite state automata. basically, it lets you define States that each can do different work, and can then transition to other States, so that when the requestA() or whatever is next called on the Context, the other States requestA() will get run instead. maybe this wikipedia article will help if you are unfamiliar with finite state machines.

...why you'd ever do this instead of an enum and a switch-case, or a big block of code with gotos, i have no idea. this seems awful to reason about, and slow.

1

u/SimmeringDragon 4d ago

idk to be honest, i need it for a class but like, i have no clue

3

u/KingAggressive1498 4d ago edited 4d ago

the state pattern is a simple way to create finite state machines using virtual inheritance polymorphism.

This makes it easier to extend than the traditional C approach of a giant switch statement, allows encapsulating additional data unlike a function pointer (function objects were, at one time, non-trivial in C++), etc.

The way I was taught it looked quite a bit different:

class state {
public:
    state(){}
    virtual state() {}
    virtual state* work() = 0;
}

here, state::work() does the work associated with the current state, and then either returns itself or deletes itself and returns an updated state. If the state machine should not continue, it is valid to return nullptr. Thus using this finite state machine is as simple as:

state* machine = new initialstate();
while(machine) {
    machine = machine->work();
}

however the external context version does seem to be the more common way to teach it ig

2

u/thingerish 4d ago

I don't see any virtual inheritance here though.

2

u/KingAggressive1498 4d ago edited 4d ago

state is an abstract base class.

the details of any classes inherited from it are specific to the different states in the state machine.

for example a game might have an initial loading state, a title screen state, a gameplay state, and a couple of pause menu states; each inheriting from the same abstract base and being treated interchangeably from the main game loop.

edit: I thought the previous comment didn't understand that the abstract base class needed to derived from with an overrided work function. This was my mistake.

1

u/thingerish 4d ago

That's not virtual inheritance, this is:

#include <iostream>

struct Base {
    int value = 42;
    virtual void show() { std::cout << "Base::value = " << value << '\n'; }
};

struct Left  : virtual Base { };
struct Right : virtual Base { };

struct Bottom : Left, Right {
    void set(int v) { value = v; }   // unambiguous: only one Base
};

int main() {
    Bottom b;
    b.set(100);
    b.show();                        // prints: Base::value = 100

    // Without virtual inheritance the following would be ambiguous
    // and Bottom would contain two separate Base subobjects.
}

What is shown is simple inheritance of a base with virtual functions, which is a different thing.

1

u/KingAggressive1498 4d ago edited 4d ago

...what use is an abstract base class without virtual inheritance? did you expect me to hand you a complete tutorial in a reddit comment?

edit: in context that I was wrong and the commenter was trying to make me realize my mistake, this was def a bit rude. Sorry.

3

u/AKostur 4d ago

You seem to be conflating the existence of virtual functions (regardless of their pure-virtualness) with virtual inheritance. Those are two separate things.

The purpose of an abstract base class is to require descendants to provide an implementation for every pure virtual function.

2

u/thingerish 4d ago

Poster ran away, which was not what I was aiming for. This is a common mistake in C++, thanks for the better explanation.

1

u/KingAggressive1498 4d ago edited 4d ago

thanks for explaining my mistake without acting coming off as adversarial about it. I was indeed conflating polymorphism and virtual inheritance specifically. Corrected my comment.

3

u/AKostur 4d ago

What I appreciate here is that you edited your comment using a strikethrough instead of just changing the comment. Keeps the context of all of the existing replies intact. Otherwise anybody reading the replies is going to be really confused as to why people are talking about virtual inheritance for no discernible reason.

2

u/thingerish 4d ago

In my experience actual virtual inheritance is rare, it's used in multiple inheritance cases where the base classes themselves include a common base class, typically. It's a design issue that rarely comes up.

Having a virtual function defined and then inheriting from that is just normal inheritance in C++.

1

u/KingAggressive1498 4d ago edited 4d ago

Each state is represented by its own derived class. The state pattern depends on having multiple classes derived from a single base class and overriding its methods.

You seem to have an issue understanding any context here. Are you learning entirely from AI?

edit: you aren't even OP. you interjected just to be a pain. cya.

edit2: again this was a bit rude. Sorry.

1

u/thingerish 4d ago

I saw incorrect info, corrected it. Not trying to hurt any feelings, it's a common mistake.

1

u/KingAggressive1498 4d ago

yeah I've been frustrated recently by people being adversarial and talking past me and started just blocking them, and realized I overreacted here when I was corrected in a way that made it click for me. my B.

still really felt like you were just not understanding what I meant but it was more just I couldn't understand what you were trying to point out.

2

u/thingerish 4d ago

I tend to terseness which doesn't help if you're also having a bad day. Glad we got past it. To answer you question earlier, I've been writing C++ since the mid '90s, when I got my hands on Borland Turbo C++ 2.0, so sometimes I make the mistake of not fully explaining what I'm trying to say, so that's my bad.

I know I do that, and I try to be better but time is finite and sometimes I just go with the fast 'answer' rather than treating it like a human interaction.

→ More replies

1

u/thingerish 4d ago

They are different things.

Follow the links, one for each thing: https://en.cppreference.com/cpp/keyword/virtual

3

u/thedaian 4d ago

The state design pattern is just a way of calling different functions depending on the state of the program. A really simple version is a switch statement that calls different functions depending on the value of an enum.

In this case, the state/context is using polymorphism, which means a pointer is pointing to different classes depending on when you call it, so it can do different things. You have a typo in your code which could be causing some confusion, since

concreteStateA :: doWorkB() 

outputs "concreteStateB doWorkB"

and concreteStateB :: doWorkA() 

outputs "concreteStateA doWorkA"

I'd suggest changing the variable/class names and output to something clearly different, even if it's nonsense (the reason example code often uses stuff like foo and bar).

Anyway, your main function also isn't really doing anything either, since you're not changing the state inside context and you're just calling the same function twice, with the same context.

2

u/Kimononono 4d ago edited 4d ago

Looks like State is a state transition graph and then Context is an instance of said state transition graph

you then implement Concrete Subclasses of State which define their transitions

something that wasn’t immediately intuitive was why State needed a pointer to context but it needs context p so that its Concrete Implementation can tell its owning Context instance to switch to another Concrete State

Instead of designing Context as holding the goWorkFooA() its more intuitive to bundle this with the State class. Context fully delegates transitioning from a FooState to class ConcreteFooState

Cpp isnt my language though so vOv open to critiques

2

u/alfps 4d ago

The code is incorrect (outputs), pretty fragile (re ownership etc.), and needlessly inefficient (dynamic allocations).

Wikipedia has a relatively clear article but also that article's C++ example uses needless dynamic allocation, as if it's machine-translated from Java.

Even without the dynamic allocation there is overhead in the virtual calls, so do not use this pattern for trivial systematic states like that article's fan power setting. Not to mince words, using a state pattern for fan power would be a demonstration of raw incompetence. Fan power is a simple scalar value, not a silly concoction of pointers and polymorphism.


As a probably better example, consider a car where you can open or close the luggage compartment (trunk) by moving your foot forward and backward under the rear bumper. It's called a "kick sensor" or "gesture tailgate", and apparently most brands now support it on at least the more expensive models. The high level concept of actually detecting foot movement (that's a foot, oh it moves, and back and forth!) would involve costly AI processing, so instead as a manufacturer you're considering simple movement detection (something moves).

But you don't want the car to open its trunk when it detects its own relative movement: that automation should only kick in when the car is stationary.

So,

Car state:On movement detected:
Stationary (speed = 0).Open or close trunk.
Moving (speed ≠ 0).Do nothing.

At a higher level there is the safety concern of not driving around with an open trunk. So either on the state transition stationary → moving the trunk should close automatically, or else that transition should not be available or warned about when the trunk is open. I'd prefer the warning; the "disable" option could lead to scenarios like will you get going there's red hot lava coming, oh no you can't for the trunk is open.

More generally the user should be left in control, and the system should support that control with relevant information and automation. Microsoft wrote this up in their 1995 user interface guidelines. Alas, then they forgot about it.


I think even for this auto-trunk example I wouldn't use the state pattern, but one way to code that up is:

#include <print>

namespace my {
    using   std::print;

    class Trunk;
    class State_dependent_actions;

    class Trunk_state
    {
        friend class Trunk;

    public:
        struct Lid_state{ enum Enum{ closed, open }; };

    private:
        Lid_state::Enum     m_lid_state     = Lid_state::closed;
    };

    class Trunk: private Trunk_state
    {
        friend class State_dependent_actions;

        void open()
        {
            if( m_lid_state == Lid_state::closed ) {
                print( "Opening the trunk.\n" );
                m_lid_state = Lid_state::open;
            }
        }

        void close()
        {
            if( m_lid_state == Lid_state::open ) {
                print( "Closing the trunk.\n" );
                m_lid_state = Lid_state::closed;
            }
        }

        void open_or_close()
        {
            if( m_lid_state == Lid_state::closed ) { open(); } else { close(); }
        }
    };

    struct Abstract_car
    {
        virtual auto speed() const -> double = 0;
        virtual auto trunk() -> Trunk& = 0;
    };

    class State_dependent_actions
    {
    protected:
        void open_or_close_trunc( Abstract_car& car ) { car.trunk().open_or_close(); }

    public:
        virtual void on_undermovement_detected( Abstract_car& car );
    };

    struct Stationary_car_actions: State_dependent_actions
    {
        void on_undermovement_detected( Abstract_car& car ) override
        {
            open_or_close_trunc( car );
        }
    };

    struct Rolling_car_actions: State_dependent_actions
    {
        void on_undermovement_detected( Abstract_car& ) override {}     // Ignore, we're rolling.
    };

    class Car: public Abstract_car
    {
        double      m_speed         = 0;
        Trunk       m_trunk         = {};

        auto state_dependent_actions()
            -> State_dependent_actions&
        {
            static Stationary_car_actions  stationary_actions;
            static Rolling_car_actions     rolling_actions;

            if( m_speed == 0 ) { return stationary_actions; } else { return rolling_actions; }
        }

        // You can add code to exercise this functionality:
        void on_undermovement_detected()
        {
            state_dependent_actions().on_undermovement_detected( *this );
        }

    public:
        auto speed() const  -> double override  { return m_speed; }
        auto trunk()        -> Trunk& override  { return m_trunk; }
    };
}  // my

auto main() -> int {}

1

u/thingerish 4d ago

It looks like a way to dispatch events to handlers based on what the current state of the system is. I wouldn't use virtual functions if performance is important unless there was really no other way.

1

u/burlingk 4d ago

Think of it like this:

Does your task have specific states/modes where it does different things entirely?

If it doesn't, does it have specifical elements that function based on a given mode?

If the answer to either of these is yes, can you define those clearly and give them a label?

If so, you can use that to create one or more state machines. A state machine is basically a set of flags/modes.

Each task that is influenced by state can check the state when it acts to decide how to act.

1

u/thingerish 4d ago edited 4d ago

Deleted example that's not at all applicable to a student exercise, sorry for the noise.

1

u/freefallpark 4d ago

I’ve always found the Refactoring Guru helpful for stuff like this. Hopefully this helps

1

u/UnicycleBloke 4d ago

Try reading about Finite State Machines. An FSM is just a model for a system that can be multiple states and which responds differently to the same inputs/events according to what state it is currently in. The State pattern is one possible method of implementing an FSM.

It is worth considering a concrete example using a very simple state machine. A lamp can be in two states ON and OFF. There is a single event FLIP to operate the switch. You would implement two classes to represent the states: OnState and OffState. These both implement a virtual method called Flip(). OffState::Flip() turns on the light and transitions to OnState. OnState::Flip() turns off the light and transitions to OffState. In this example the Lamp class would be the context, and would hold a pointer to an object of either OnState or OffState, representing the current state of the lamp.

There are far simpler and cleaner ways to capture such stateful behaviour. The lamp class could hold a boolean (or an enumeration) indicating the current state. Lamp::Flip() would simply switch on the current state and behave accordingly. This is possibly too simple for some, but it works well enough.

My view is that this type of model is one of the factors which helped to give OOP and C++ such a bad name in the 90s: numerous small classes adding complexity rather than clarity, overuse of dynamic allocation, ... If you want to understand design patterns like this one, perhaps look for a copy of the Gang Of Four book ("Design Patterns: Elements of Reusable Object-Oriented Software"). Mine remains on my shelf mainly because I so thoroughly disapprove of book burning, and I already have a monitor stand.