C++ ステートマシンの簡単な作り方

ステートマシンとは


ステートマシンは、状態遷移を実装するために使います。実装方法は、

  • ステートマシンのクラスのメンバー変数を状態として定義して状態のメンバー変数を切り替えることで管理する方法。
  • 状態をクラスとして定義してクラスを切り替えることで管理する方法。

など、様々な方法が考えられます。
サンプルをスイッチのOn状態とOff状態を切り替えるステートマシンを両方で実装しました。

サンプル

スイッチを切り替えることでOff状態からOn状態に遷移させるプログラムです。
遷移前の状態と遷移後の状態を出力させることで、状態遷移が成功していることを確認します。

メンバー変数で管理する場合

実装方法

状態をenumのメンバー変数で定義し、メンバー関数をSwitch文で分岐させることでステートマシンを実装しました。

メリット

現在のステートマシンに、新しい状態を拡張する場合enumに要素を追加するだけで良く、全体像を把握しやすいです。

デメリット

ネストが深くなりやすく、複雑なステートマシンを組みにくいです。

メイン関数

//main.cpp
#include<iostream>
#include"switch.h"
int main()
{
	//ステートマシン初期化(Off)
	Switch s(SwitchState::Off);
	//状態を出力
	s.Put();
	//スイッチを切り替える(Off→On)
	s.Change();
	//状態を出力
	s.Put();
}

ステートマシンクラス

//switch.h
#pragma once
#include<iostream>
enum SwitchState
{
	On = 0,
	Off
};
class Switch
{
public:
	Switch() = delete;
	Switch(SwitchState s) 
	{
		state = s;
	}
	void Put()
	{
		switch (state)
		{
		case SwitchState::On:
			std::cout << "現在のステイトはOn" << std::endl;
			break;
		case SwitchState::Off:
			std::cout << "現在のステイトはOff" << std::endl;
			break;
		default:
			break;
		}
	}
	void Change()
	{
		switch (state)
		{
		case SwitchState::On:
			state = SwitchState::Off;
			break;
		case SwitchState::Off:
			state = SwitchState::On;
			break;
		default:
			break;
		}
	}
private:
	SwitchState state;
};

実行結果

//実行結果
現在のステイトはOff
現在のステイトはOn

クラスを切り替えて管理する場合

実装方法

状態クラスをインターフェースクラスで定義し、具体的な状態(On,Off)は状態クラスを継承したクラスで実装します。

メリット

現在のステートマシンに、新しい状態を拡張する事も簡単なので柔軟に作りたいプログラムにあった、適切なステートマシンを作ることが出来ます。

デメリット

今回のサンプルの実装では、大きなステートマシンを実装すると、ファイルが増えすぎてしまう上に、全体像の把握が難しいという欠点があります。
状態を管理するクラスの、メンバー変数として。状態クラスを持たせる事である程度管理しやすくなります。

メイン関数

ステートマシンの状態遷移をさせています。
スイッチの状態がOnであってもOffであっても、Stateクラスでインターフェースを定義した。Put関数とSwitch関数を、よび出すことができていることがわかります。

//main.cpp
#include<iostream>
#include"stateOff.h"
int main()
{
	//ステートマシンを初期化
	State* state =new StateOff();
	
	//状態を出力
	state->Put();
	
	//スイッチを切り替える(Off→On)
	state = state->Switch();
	//状態を出力
	state->Put();
	delete state;
}

状態クラス

StateクラスはインターフェースクラスなのでStateクラスのインスタンスを作成することは出来ません。
Stateクラスを継承し、仮想関数を実装したクラスのインスタンスを作成することになります。

//state.h
#pragma once
//親クラス
class State
{
public:
	virtual void Put() = 0;//出力関数
	virtual State* Switch() = 0;//スイッチを切り替える
};
//stateOff.h
#pragma once
#include"state.h"
#include<iostream>
//Off状態のクラス
class StateOff :public State
{
public:
	void Put()
	{
		std::cout << "現在のステイトはOff" << std::endl;
	}
	State* Switch();
	
};
//stateIdle.cpp
#include"stateOff.h"
#include"stateOn.h"
State * StateOff::Switch()
{
	StateOn* ret = new StateOn();
	delete this;
	return ret;
}
//stateOn.h
#pragma once
#include"state.h"
#include<iostream>
//On状態のクラス
class StateOn :public State
{
public:
	void Put()
	{
		std::cout << "現在のステイトはOn" << std::endl;
	}
	State* Switch();
};
//stateOn.cpp
#include"stateOn.h"
#include"stateOff.h"
State* StateOn::Switch()
{
	StateOff* ret = new StateOff();
	delete this;
	return ret;
}

実行結果

出力したステイトが。OffからOnに代わっていることが確認できました。

//実行結果
現在のステイトはOff
現在のステイトはOn

LINEスタンプ

LINE絵文字

LINE着せ替え

まだ間に合う!LINEスタンプで月5万売り上げる方法

LINEクリエイターなろう!

procreateの記事一覧

PowerPointの記事一覧

Birthday Color iphone icons