[2026] C++ State Pattern: Complete Guide | Finite State Machines & Behavior

[2026] C++ State Pattern: Complete Guide | Finite State Machines & Behavior

이 글의 핵심

State pattern in C++: replace giant switch/if chains with state objects—traffic lights, TCP, game AI, vending machines; Strategy vs State for English search.

Compare Strategy vs State alongside other behavioral patterns in C++ behavioral patterns #20-1 and overview #20-2.

What is State Pattern? why you need it

Problem Scenario: Exploding conditional statement

Problem: If the behavior depends on the state of the object, you have a huge if-else. 다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// Bad example: exploding conditional statement
class TCPConnection {
    enum State { CLOSED, LISTEN, ESTABLISHED };
    State state = CLOSED;
    
    void open() {
        if (state == CLOSED) {
// Transition to LISTEN
        } else if (state == LISTEN) {
// already opened
        } else if (state == ESTABLISHED) {
// error
        }
    }
    
    void close() {
        if (state == CLOSED) {
// error
        } else if (state == LISTEN) {
// Transition to CLOSED
        } else if (state == ESTABLISHED) {
// Transition to CLOSED
        }
    }
};

Solution: State Pattern encapsulates each state into a class. Context holds the current State object and delegates requests to the State. 다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// Good example: State Pattern
// 타입 정의
class TCPState {
public:
    virtual void open(TCPConnection& conn) = 0;
    virtual void close(TCPConnection& conn) = 0;
    virtual ~TCPState() = default;
};
class ClosedState : public TCPState {
    void open(TCPConnection& conn) override {
        conn.setState(std::make_unique<ListenState>());
    }
    void close(TCPConnection& conn) override {
// error
    }
};
class TCPConnection {
    std::unique_ptr<TCPState> state;
public:
    void open() { state->open(*this); }
    void close() { state->close(*this); }
    void setState(std::unique_ptr<TCPState> s) { state = std::move(s); }
};

아래 코드는 mermaid를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// 실행 예제
stateDiagram-v2
    [*] --> Closed
    Closed --> Listen: open()
    Listen --> Established: accept()
    Established --> Closed: close()
    Listen --> Closed: close()

index

  1. Basic structure
  2. State transition diagram
  3. Game AI example
  4. Frequently occurring problems and solutions
  5. Production Patterns
  6. Complete example: vending machine

1. basic structure

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <iostream>
#include <memory>
class TrafficLight;
class State {
public:
    virtual void handle(TrafficLight& light) = 0;
    virtual std::string name() const = 0;
    virtual ~State() = default;
};
class TrafficLight {
public:
    TrafficLight();
    
    void setState(std::unique_ptr<State> s) {
        state = std::move(s);
        std::cout << "State: " << state->name() << '\n';
    }
    
    void change() {
        state->handle(*this);
    }
    
private:
    std::unique_ptr<State> state;
};
class RedState : public State {
public:
    void handle(TrafficLight& light) override;
    std::string name() const override { return "Red"; }
};
class GreenState : public State {
public:
    void handle(TrafficLight& light) override;
    std::string name() const override { return "Green"; }
};
class YellowState : public State {
public:
    void handle(TrafficLight& light) override;
    std::string name() const override { return "Yellow"; }
};
void RedState::handle(TrafficLight& light) {
    light.setState(std::make_unique<GreenState>());
}
void GreenState::handle(TrafficLight& light) {
    light.setState(std::make_unique<YellowState>());
}
void YellowState::handle(TrafficLight& light) {
    light.setState(std::make_unique<RedState>());
}
TrafficLight::TrafficLight() {
    state = std::make_unique<RedState>();
    std::cout << "Initial state: " << state->name() << '\n';
}
int main() {
    TrafficLight light;
    light.change();  // Red -> Green
    light.change();  // Green -> Yellow
    light.change();  // Yellow -> Red
}

2. state transition diagram

TCP connection status

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <iostream>
#include <memory>
class TCPConnection;
class TCPState {
public:
    virtual void open(TCPConnection& conn) = 0;
    virtual void close(TCPConnection& conn) = 0;
    virtual void acknowledge(TCPConnection& conn) = 0;
    virtual std::string name() const = 0;
    virtual ~TCPState() = default;
};
class TCPConnection {
public:
    TCPConnection();
    
    void setState(std::unique_ptr<TCPState> s) {
        state = std::move(s);
        std::cout << "State: " << state->name() << '\n';
    }
    
    void open() { state->open(*this); }
    void close() { state->close(*this); }
    void acknowledge() { state->acknowledge(*this); }
    
private:
    std::unique_ptr<TCPState> state;
};
class ClosedState : public TCPState {
public:
    void open(TCPConnection& conn) override;
    void close(TCPConnection& conn) override {
        std::cout << "Already closed\n";
    }
    void acknowledge(TCPConnection& conn) override {
        std::cout << "Error: not open\n";
    }
    std::string name() const override { return "Closed"; }
};
class ListenState : public TCPState {
public:
    void open(TCPConnection& conn) override {
        std::cout << "Already listening\n";
    }
    void close(TCPConnection& conn) override;
    void acknowledge(TCPConnection& conn) override;
    std::string name() const override { return "Listen"; }
};
class EstablishedState : public TCPState {
public:
    void open(TCPConnection& conn) override {
        std::cout << "Already established\n";
    }
    void close(TCPConnection& conn) override;
    void acknowledge(TCPConnection& conn) override {
        std::cout << "Data transfer...\n";
    }
    std::string name() const override { return "Established"; }
};
void ClosedState::open(TCPConnection& conn) {
    conn.setState(std::make_unique<ListenState>());
}
void ListenState::close(TCPConnection& conn) {
    conn.setState(std::make_unique<ClosedState>());
}
void ListenState::acknowledge(TCPConnection& conn) {
    conn.setState(std::make_unique<EstablishedState>());
}
void EstablishedState::close(TCPConnection& conn) {
    conn.setState(std::make_unique<ClosedState>());
}
TCPConnection::TCPConnection() {
    state = std::make_unique<ClosedState>();
    std::cout << "Initial state: " << state->name() << '\n';
}
int main() {
    TCPConnection conn;
    conn.open();         // Closed -> Listen
    conn.acknowledge();  // Listen -> Established
    conn.close();        // Established -> Closed
}

3. Game AI Example

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <iostream>
#include <memory>
class Enemy;
class AIState {
public:
    virtual void update(Enemy& enemy) = 0;
    virtual std::string name() const = 0;
    virtual ~AIState() = default;
};
class Enemy {
public:
    Enemy(int hp, int dist) : health(hp), distanceToPlayer(dist) {
        state = std::make_unique<PatrolState>();
    }
    
    void setState(std::unique_ptr<AIState> s) {
        state = std::move(s);
        std::cout << "AI State: " << state->name() << '\n';
    }
    
    void update() {
        state->update(*this);
    }
    
    int getHealth() const { return health; }
    int getDistance() const { return distanceToPlayer; }
    void takeDamage(int dmg) { health -= dmg; }
    void setDistance(int dist) { distanceToPlayer = dist; }
    
private:
    std::unique_ptr<AIState> state;
    int health;
    int distanceToPlayer;
};
class PatrolState : public AIState {
public:
    void update(Enemy& enemy) override {
        std::cout << "Patrolling...\n";
        if (enemy.getDistance() < 10) {
            enemy.setState(std::make_unique<ChaseState>());
        }
    }
    std::string name() const override { return "Patrol"; }
};
class ChaseState : public AIState {
public:
    void update(Enemy& enemy) override {
        std::cout << "Chasing player...\n";
        if (enemy.getDistance() > 20) {
            enemy.setState(std::make_unique<PatrolState>());
        } else if (enemy.getDistance() < 3) {
            enemy.setState(std::make_unique<AttackState>());
        } else if (enemy.getHealth() < 20) {
            enemy.setState(std::make_unique<FleeState>());
        }
    }
    std::string name() const override { return "Chase"; }
};
class AttackState : public AIState {
public:
    void update(Enemy& enemy) override {
        std::cout << "Attacking!\n";
        if (enemy.getDistance() > 5) {
            enemy.setState(std::make_unique<ChaseState>());
        } else if (enemy.getHealth() < 20) {
            enemy.setState(std::make_unique<FleeState>());
        }
    }
    std::string name() const override { return "Attack"; }
};
class FleeState : public AIState {
public:
    void update(Enemy& enemy) override {
        std::cout << "Fleeing...\n";
        if (enemy.getDistance() > 30) {
            enemy.setState(std::make_unique<PatrolState>());
        }
    }
    std::string name() const override { return "Flee"; }
};
int main() {
    Enemy enemy(100, 15);
    
    enemy.update();  // Patrol
    enemy.setDistance(5);
    enemy.update();  // Chase
    enemy.setDistance(2);
    enemy.update();  // Attack
    enemy.takeDamage(85);
    enemy.update();  // Flee
}

4. Frequently occurring problems and solutions

Problem 1: Circular dependencies

Symptom: Compilation error. Cause: State refers to Context, Context refers to State. 아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 코드를 직접 실행해보면서 동작을 확인해보세요.

// ✅ SOLVED: Forward declaration
class Context;
class State {
    virtual void handle(Context& ctx) = 0;
};

Problem 2: Cost of creating a State object

Symptom: Poor performance. Cause: State object is created for each transition. 아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 코드를 직접 실행해보면서 동작을 확인해보세요.

// ✅ Solved: Flyweight (shared)
class StateManager {
    static RedState redState;
    static GreenState greenState;
public:
    static State* getRed() { return &redState; }
    static State* getGreen() { return &greenState; }
};

5. production pattern

Pattern 1: State History

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Context {
    std::unique_ptr<State> state;
    std::vector<std::string> history;
    
public:
    void setState(std::unique_ptr<State> s) {
        history.push_back(state->name());
        state = std::move(s);
    }
    
    void printHistory() const {
        for (const auto& s : history) {
            std::cout << s << " -> ";
        }
        std::cout << state->name() << '\n';
    }
};

6. Complete example: vending machine

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <iostream>
#include <memory>
class VendingMachine;
class State {
public:
    virtual void insertCoin(VendingMachine& vm) = 0;
    virtual void selectProduct(VendingMachine& vm) = 0;
    virtual void dispense(VendingMachine& vm) = 0;
    virtual std::string name() const = 0;
    virtual ~State() = default;
};
class VendingMachine {
public:
    VendingMachine();
    
    void setState(std::unique_ptr<State> s) {
        state = std::move(s);
        std::cout << "[State: " << state->name() << "]\n";
    }
    
    void insertCoin() { state->insertCoin(*this); }
    void selectProduct() { state->selectProduct(*this); }
    void dispense() { state->dispense(*this); }
    
private:
    std::unique_ptr<State> state;
};
class NoCoinState : public State {
public:
    void insertCoin(VendingMachine& vm) override;
    void selectProduct(VendingMachine& vm) override {
        std::cout << "Insert coin first\n";
    }
    void dispense(VendingMachine& vm) override {
        std::cout << "Insert coin first\n";
    }
    std::string name() const override { return "NoCoin"; }
};
class HasCoinState : public State {
public:
    void insertCoin(VendingMachine& vm) override {
        std::cout << "Coin already inserted\n";
    }
    void selectProduct(VendingMachine& vm) override;
    void dispense(VendingMachine& vm) override {
        std::cout << "Select product first\n";
    }
    std::string name() const override { return "HasCoin"; }
};
class DispensingState : public State {
public:
    void insertCoin(VendingMachine& vm) override {
        std::cout << "Please wait\n";
    }
    void selectProduct(VendingMachine& vm) override {
        std::cout << "Please wait\n";
    }
    void dispense(VendingMachine& vm) override;
    std::string name() const override { return "Dispensing"; }
};
void NoCoinState::insertCoin(VendingMachine& vm) {
    std::cout << "Coin inserted\n";
    vm.setState(std::make_unique<HasCoinState>());
}
void HasCoinState::selectProduct(VendingMachine& vm) {
    std::cout << "Product selected\n";
    vm.setState(std::make_unique<DispensingState>());
}
void DispensingState::dispense(VendingMachine& vm) {
    std::cout << "Dispensing product...\n";
    vm.setState(std::make_unique<NoCoinState>());
}
VendingMachine::VendingMachine() {
    state = std::make_unique<NoCoinState>();
    std::cout << "[Initial state: " << state->name() << "]\n";
}
int main() {
    VendingMachine vm;
    vm.insertCoin();
    vm.selectProduct();
    vm.dispense();
}

organize

conceptDescription
State PatternEncapsulating state-specific behavior into classes
PurposeRemoval of conditional statements, clarification of state transitions
StructureContext, State, ConcreteState
AdvantagesOCP compliance, state independence, readability
DisadvantagesClass increases, state transition becomes more complex
Use CaseFSM, TCP, game AI, vending machine
The State Pattern is a powerful pattern for implementing a state machine in an object-oriented way.

FAQ

Q1: When do I use State Pattern?

A: Used when the operation varies depending on the state and the conditional statement is complex.

Q2: What is the difference from Strategy Pattern?

A: Strategy focuses on algorithm replacement, State focuses on state transition.

Q3: What is the cost of creating a State object?

A: You can reduce costs by sharing State objects with the Flyweight pattern.

Q4: What is the status history?

A: Just record the previous state with std::vector.

Q5: What about asynchronous state transitions?

A: Asynchronous transfer is possible using std::async or an event queue.

Q6: What are the State Pattern learning resources?

A:

  • “Design Patterns” by Gang of Four
  • “Game Programming Patterns” by Robert Nystrom
  • Refactoring Guru: State Pattern One-line summary: State Pattern allows for a clean implementation of state machines. Next, it would be a good idea to read Decorator Pattern.

Good article to read together (internal link)

Here’s another article related to this topic.

Practical tips

These are tips that can be applied right away in practice.

Debugging tips

  • If you run into a problem, check the compiler warnings first.
  • Reproduce the problem with a simple test case

Performance Tips

  • Don’t optimize without profiling
  • Set measurable indicators first

Code review tips

  • Check in advance for areas that are frequently pointed out in code reviews.
  • Follow your team’s coding conventions

Practical checklist

This is what you need to check when applying this concept in practice.

Before writing code

  • Is this technique the best way to solve the current problem?
  • Can team members understand and maintain this code?
  • Does it meet the performance requirements?

Writing code

  • Have you resolved all compiler warnings?
  • Have you considered edge cases?
  • Is error handling appropriate?

When reviewing code

  • Is the intent of the code clear?
  • Are there enough test cases?
  • Is it documented? Use this checklist to reduce mistakes and improve code quality.

Keywords covered in this article (related search terms)

This article will be helpful if you search for C++, state, pattern, fsm, state-machine, behavior, etc.

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3