UE4の勉強記録

UE4の勉強の記録です。個人用です。

新しいツールバーボタンを作る(リバースエンジニア編)Part 2

<前文>

f:id:kazuhironagai77:20180225113310p:plain

で勉強しています。

今回も、ツールバーボタンのコードを分析していきます。前回、CookbookCommandクラスの分析まで、終わりましたので、UE4CookbookEditorクラスの分析を行います。前回は、予想外に時間を取られてしまったので、今回は、テキパキとやりたいです。

<本文>

<目的>

前々回、作成したツールバーボタンを作成するのコードを分析する事で、「どのようにUE4エディターで、ツールバーボタンを作成するかを理解する。」事が目的です。理解の目安としては、

  • 全体の流れを大まかに説明出来る。
  • 一つ一つのコードが何を行っているのかをコメントできる。
  • 部分的にコードを変えて、結果をある程度変える事が出来る。
  • アイコンを変える。

<方法>

リバースエンジニアリングとは言いましたが、特にリバースエンジニアを勉強した事はないので、以下の事をやって見ます。

  • 教科書のhow it works…をよく読む。
  • 使用されたすべてのメンバー関数、クラスをUE4APIで調べる。
  • コードを少しだけ変えて結果の変化を観察する。

前回、この方法で、かなり理解出来たので、今回も同じ方法で行おうと思います。

<仮説>

f:id:kazuhironagai77:20180225113411p:plain

CookbookCommandsクラスがSlateモジュールの持つコマンドをC++側から使用出来るようにする事、UE4CookbookEditorクラスが、実際に使用可能になったコマンドを用いてUE4エディターのツールボタンを作成しているという仮説に基づいて、個々のコードを検証していく。

<検証1:全体の検証>

f:id:kazuhironagai77:20180225113500p:plain

f:id:kazuhironagai77:20180225113526p:plain

コードを個々に検証する前に、UE4CookbookEditorヘッダーファイルにある二つのメンバー関数、MyButton_Clicked(), AddToolbarExtension()について、一つ試したい事があります。

f:id:kazuhironagai77:20180225113604p:plain

この二つのメンバー関数は、宣言だけでなく、その定義まで、ヘッダーファイルで行っています。そのせいで、UE4CookbookEditorヘッダーファイルが複雑な形になっているのですが、本当にこの二つのメンバー関数の定義をヘッダーファイルで行わないといけないのかを、まず検証してみました。

f:id:kazuhironagai77:20180225113657p:plain

f:id:kazuhironagai77:20180225113719p:plain

まず、上記に示した通りに、単に二つの関数の定義をソースファイルに移して、ビルトし、デバック実行してみました。

f:id:kazuhironagai77:20180225113759p:plain

すると、何も問題なく、UE4エディターが起動しました。何か拍子抜けですが、UE4CookbookEditor クラスのメンバー関数であるMyButton_Clicked(), AddToolbarExtension()の定義は普通にソースファイルで行えるようです。これで、UE4CookbookEditor クラスがかなり見やすくなりました。

更に、教科書のhow it works…のUE4CookbookEditor クラスについての解説を読んでみると、以下の文章に出会いました。

f:id:kazuhironagai77:20180225113841p:plain

この一文は、教科書のhow it works…のCookbookCommandsクラスの解説の最後で、UE4CookbookEditor クラスについての解説の最初の位置にあります。つまり、今まで解説して来たCookbookCommandsクラスの総括と、これから解説するUE4CookbookEditor クラスの簡単な紹介がされている箇所になります。

22.私たちはコマンド一式を持つにいたったが、まだ、この(ゲーム)エンジンに、ツールバーを表示するコマンド一式を加えたいと伝えてはいない。またボタンがクリックされた時に、実際に何がおきるのかも設定していない。それらをするためには、モジュールが起動する時に、幾つかの(オブジェクトを)初期化をする必要がある。では、そのためにStartupModucle/ShutdownModule 関数にコードを加えよう。

上記に適当な日本語訳を補足しましたが、CookbookCommandsクラスがコマンド一式を持つためのクラスで、UE4CookbookEditor クラスのStartupModucle/ShutdownModule 関数がツールバーを表示するコマンド一式を加え、ボタンがクリックされた時に、実際に何がおきるのかも設定する関数であると述べています。つまり、前回、私が考えた仮説、

f:id:kazuhironagai77:20180225113921p:plain

と同じ事をここで述べていたのです。私の仮説が支持された。というか、教科書にそのまま書かれていたわけでした。前回のブログを書く前に、教科書のhow it works…は読んだはずですが理解はしていなかったようです。「教科書は7回読んで、初めて理解出来る。」とは、よく聞く格言ですがまさしく真実ですね。

<検証2:個々の検証>

では、初めにUE4CookbookEditor クラスのヘッダーファイルから見ていきましょう。まずは、インクルードされているヘッダーファイルを見てみます。

f:id:kazuhironagai77:20180225114026p:plain

Engine.hはUE4のAPI によると、UEngineクラスをインクルードするために必要なようです。Remarkには、

f:id:kazuhironagai77:20180225114214p:plain

とあります。

すべてのエンジンクラスのアブストラクトな基底クラスで、エディターやゲームシステムの決定的なシステムの管理を担当している。また、一定のエンジンシステムのデファルトクラスを定義している

と言う事でしょうか?つまり、UE4のライブラリーを使うためには、必ずインクルードする必要があると考えられると思います。

ここで思いついたのですが、いつもインクルードしているのは、EngineMinimal.hです。Engine.hが古いversionで、新しいversionがEngineMinimal.hなのかな、と勝手に思っていましたが、ちょっと調べてみます。UE4AnswerHubに、詳しい説明がありました。要約すると、Engine.h>EngineMinimal.hという関係で、Engine.hをインクルードすれば、EngineMinimal.hのクラスはすべてインクルードされるが、その分重くなるのだそうです。

ModuleManager.hはAPIによると、FModuleManagerクラスを宣言し、FModuleManagerクラスはremarkによれば、

f:id:kazuhironagai77:20180225114303p:plain

モジュールマネージャーを実装する。そのモジュールマネージャーは、現在、ロードされているモジュールの記録と、モジュールのロードとアンロードに使われます。このシングルトンは、FModuleMagener::Get()を使用することで、アクセス出来ます。

とあります。モジュールを使用するのですから、当然必要なクラスでしょう。

次のCookbookCommands.hは、前回検証したクラスで、UICommnadをC++側から使うためのクラスだと結論づけられました。このクラスで作成したオブジェクト変数、MyButtonを、このUE4CookbookEditor クラスで使ってUICommand を間接的に使用するはずです。

IMainFrameModule.hは、前々回、MainFrameクラスをインクルードするために、加えたヘッダーファイルで、Packet Publish社のサンプルコードには、MainFrame.hとなっていて、エラーを吐き出していた懐かしい箇所です。UE4のAPIによると

f:id:kazuhironagai77:20180225114354p:plain

と説明されています。メインフレームのモジュールのインターフェイスならば、ツールバーボタンを拡張するためには必要になるのでしょう。このMainFrameクラスのインクルードは、アドレスを全部書いてあるのですが、前々回、何でそうしたのか覚えていません。

f:id:kazuhironagai77:20180225114430p:plain

ビルドして実行してみると、

f:id:kazuhironagai77:20180225114507p:plain

普通に動きました。ただし、Visual studio は、エラーをはいています。このエラーが嫌で、IMainFrameModule.hのアドレスを全部書いたのかもしれません。

AssetTypeAction_Base.hファイルは、FAssetTypeActions_Baseクラスを作るためのヘッダーファイルです。UE4のAPIには、

f:id:kazuhironagai77:20180225114542p:plain

と書かれています。どんなことに使用するかはまだ不明です。

UnrealEd.h ファイルは、実は何のためのファイルか分かりません。UE4のAPIにはRemarkがないですし、実際のUnrealEd.h ファイルは、

f:id:kazuhironagai77:20180225114611p:plain

となっていて、ひたすら、ヘッダーファイルをインクルードしています。

最後の、MultiBoxExtender.hは、FToolBarExtensionDelegateを使うために、インクルードするようです。FToolBarExtensionDelegateは、UE4のAPIのremarkによると、

f:id:kazuhironagai77:20180225114708p:plain

とあり、ツールバーに新しいアイテムを加える時は、必ず必要になるようです。

では、内容のほうを、見ていきましょう。

f:id:kazuhironagai77:20180225114916p:plain

まず、FUE4CookbookEditorModuleクラスは、IModuleInterfaceを継承しています。IModuleInterfaceのAPIには、

f:id:kazuhironagai77:20180225114950p:plain

と書かれていますが、

f:id:kazuhironagai77:20180225115015p:plain

上記のStartupModule()とShutdownModule()メンバー関数をオーバーライドするのに、不可欠なインターフェイスですから、継承するのは、当然でしょう。勿論、その後で、StartupModule()とShutdownModule()メンバー関数をオーバーライドしています。

f:id:kazuhironagai77:20180225115058p:plain

その次の行は、上記のオブジェクト変数の宣言がされています。FExtenderクラスは、UE4のAPIによるとMultiBoxExtender.hで宣言されるクラスの一つでツールバーや、メニューバーを拡張するのに使用するそうです。FExtensionBaseのオブジェクト変数は、FExtenderのメンバー関数のパラメーターとして必要になるそうです。

f:id:kazuhironagai77:20180225115127p:plain

最後のMyButton_Clicked()、AddToolbarExtension()メンバー関数は、StartupModule()メンバー関数で使用するための関数と考えられます。その名前から、MyButton_Clicked()が、ボタンをクリックされた時のアクションの指定、AddToolbarExtension()が、ツールバーの拡張を担当していると考えられます。

ヘッダーファイルはこんな感じなのでしょうか?では、次にソースファイルを見ていきましょう。

f:id:kazuhironagai77:20180225115210p:plain

まずは、インクルードされているヘッダーファイルから見ていきましょう。UE4CookbookEditor.hファイルは当然インクルードが必要ですが、MultiBoxExtender.hファイルとCookbookCommands.hファイルはUE4CookbookEditor.hファイルですでにインクルードされています。いるはずないです。省いてみましょう。

f:id:kazuhironagai77:20180225115308p:plain

ビルドして実行すると、

f:id:kazuhironagai77:20180225115344p:plain

勿論、正常に動きます。

LevelEditor.hファイルは、FLevelEditorModuleクラスのヘッダーファイルでUE4のAPIのremarkによると、

f:id:kazuhironagai77:20180225115417p:plain

とだけ、書かれています。レベルエディターのモジュールと言う事でしょうか?良く分からないです。ただ、今から注意した方がいいと思われる箇所が、継承部分に、

f:id:kazuhironagai77:20180225115449p:plain

IModuleInterfaceがあり、StartupModule()とShutdownModule()メンバー関数をオーバーライドして使用出来る事です。

次のSlateBasics.hファイルは、

f:id:kazuhironagai77:20180225115529p:plain

上記に一部分を載せましたが、ひたすら、ヘッダーファイルをインクルードしているだけでした。

最後のAssetToolsModule.hファイルは、UE4のAPIによるとFAssetToolsModuleクラスのヘッダーファイルです。そのAPIにはRemarkがなくて端的には何が目的で使用するのかは不明です。

このクラスも、IModuleInterfaceを継承していて、StartupModule()とShutdownModule()メンバー関数をオーバーライドして使用出来ます。

f:id:kazuhironagai77:20180225115603p:plain

次に、マクロであるIMPLEMENT_GAME_MODULE()について検証します。教科書のHow it works…に全く説明がないので、インターネットで検索したところ、前々回、エディターモジュールを作成するときにお世話になったCreating an Editor Moduleに若干の説明がありました。

f:id:kazuhironagai77:20180225115635p:plain

ここの説明によるとEditor.cppファイルには、必ずこのマクロを最初に入れなければならないそうです。そして最初のパラメーターは、ヘッダーファイルで作成したモジュールクラスの名前を、二番目のパラメーターは、uproject fileで宣言したモジュールの名前を入れるそうです。ここで、閃いたのですが、教科書の前のレシピ、「新しいエディターモジュールを作る。」でこのマクロは紹介されたはずです。と言う事は、前のレシピのHow it works…には、このマクロの説明があるかもしれません。

f:id:kazuhironagai77:20180225115707p:plain

思い切りありましたが、とりあえずこのマクロが必ず必要ぐらいの理解でここでは終わりにします。

f:id:kazuhironagai77:20180225115739p:plain

では、今回のメインディッシュであるStartModule()メンバー関数を見ていきましょう。ただし、今回初めてツールボタンをSlateモジュールのコマンドを用いて作成するわけで分からない部分は多々あると考えられます。分からない部分はそのような様式で行うと考えていきます。ただし、以下に述べる事はかなり掘り下げて検証するつもりです。

  • FCookbookCommandクラスで作成したMyButtonオブジェクト変数がどのように使用されるのか?
  • FUE4CookbookEditorModuleヘッダーファイルで作成した2つのオブジェクト変数、FExtenderクラスのオブジェクト変数であるToolbarExtenderと、FExtensionBaseのオブジェクト変数であるExtensionがどのように使用されるのか?
    1. FExtenderクラスのインスタンスであるToolbarExtenderは、ツールバーや、メニューバーを拡張するのに使用されると解説されているが、本当か? 
    2. FExtensionBaseのオブジェクト変数は、FExtenderのメンバー関数のパラメーターとして本当に使用されているか?
  • 2つのメンバー関数MyButton_Clicked()、AddToolbarExtension()メンバー関数が、どのように使用されるのか?
  • 教科書のhow it works…に説明されている事。
  • UE4APIに、載っている事

f:id:kazuhironagai77:20180225115826p:plain

まず、最初の一行であるFCookbookCommands::Register();を検証します。このコードは、前々回、UE4editor.dll がないとエラーをはき続けたコードなので、よく覚えています。FCookbookCommandsクラスの直接のメンバー関数ではなく、FCookbookCommandsクラスが継承したTCommandsクラスのメンバー関数です。UE4のTCommandsクラスのAPIによると、Register()メンバー関数は、

f:id:kazuhironagai77:20180225115857p:plain

と説明されています。

このメソッド(C++にはいわゆるJAVAのメソッドと同じ定義はなく、あえてメソッドと表現したい場合は、メンバー関数というはず。)は、コマンドを登録するのに使う。コードがインラインにならないように強制する

と言う事だそうです。だたし、to Not be inline をインラインにならないように強制すると訳すと、正しくは、not to be inline だと思うのです。さらに、前述したように、C++なのにメソッドと言う表現を使用してます。適度に参考するほうがいいのかもしれません。教科書のHow it works…には、我々が作成したFCookbookCommandクラスの関数であるRegister()を呼びます。とだけ説明されています。この説明だけでは、この関数の目的は分かりません。ここでは、このメンバー関数が必ず必要になるとだけ考えておきます。

f:id:kazuhironagai77:20180225115935p:plain

次の行は、全く想定外なコードが書いてあります。FUICommandListクラスとは何でしょうか?UE4のAPIには、Remarkがありません。教科書のHow it works…には、「コマンド一式のためのshared pointerを、MakeShareableで作成する。」とあります。このFUICommandクラスのオブジェクトは、コマンド一式へのポインターだと考えればいいのでしょうか?もしそうならば、UICommand一式を保持している、FCookbookCommandクラスで作成した、MyButtonオブジェクト変数から、その値をパスされるはずです。そうなる事を期待しつつ、先を進めましょう。

f:id:kazuhironagai77:20180225120027p:plain

この行では、前述のFUICommandListクラスのオブジェクトである、CommandListからメンバー関数であるMapActionが、呼ばれています。APIによれば

f:id:kazuhironagai77:20180225120133p:plain

マルチボックスやマウス/キーボードのインプットによって実行される一連のデリゲートにコマンド情報をマップする

と言う事でしょうか?教科書のHow it works...によると、

f:id:kazuhironagai77:20180225120207p:plain

25.コマンドリストでは、FCookbookCommnandsのメンバーであるUICommandInfoのオブジェクトと、コマンドが呼び出された時に実行したい実際の関数の間にマッピングもしくはアソシエイツを作るためMapActionを使います。

とあります。つまり、このメンバー関数はコマンドと実際に実行されるメンバー関数を、デリゲートを用いて繋げる働きをすると解釈出来ます。

更にMapAction関数のそれぞれのパラメーターに対する説明があります。

f:id:kazuhironagai77:20180225120327p:plain

f:id:kazuhironagai77:20180225120340p:plain

最初のパラメーター、INUICommandInfoは、FUICommandInfoクラスのオブジェクトを要求しています。FCookbookCommandクラスで作成したMyButtonオブジェクト変数は、FUICommandInfoクラスです。前述の予測通りに、UICommand一式を保持している、FCookbookCommandクラスで作成した、MyButtonオブジェクト変数を、パラメーターとしています。MyButtonオブジェクト変数のパスのしかたは、FCookbookCommands::Get().MyButtonとなっています。教科書のHow it works…によると、Get()を使用する事で、MyButtonを使用出来るそうです。

前述の解釈が正しければ、このパラメーターのどこかにデリゲートを用いて、実際の役割を司るメンバー関数をパスすると考えられます。デリゲートそのものについては5章のイベントとデリゲートでさんざん使用したのでここでは検証しません。では次のパラメーターを見てみましょう。FExecuteActionクラスの説明で、はっきりとデリゲートと書かれています。ここで実際の機能を持つメンバー関数をデリゲートでパスするのでしょう。実際の機能を持つメンバー関数は、勿論、MyButton_Clicked()であるはずです。では、デリゲートでどうのようにパスするのでしょうか?

f:id:kazuhironagai77:20180225120449p:plain

FExecuteActionクラスのメンバー関数であるCreateRaw()を使用するようです。APIも調べたのですが、教科書のHow it works…のほうが遥かに分かり易く説明しているのでその説明をここに載せます。

f:id:kazuhironagai77:20180225120517p:plain

29.MapAction関数の2番目のパラメーターは、コマンドが実行された時に呼び出される関数にバインドするデリゲートです。

30.UE4CookbookEditorModuleはUObjectではなく生のC++のクラスなので、staticな関数よりもメンバー関数を使いたい。そこで、生のC++のメンバー関数にバインドする新しいデリゲートを作成するためにCreateRawを使用します。

31.CreateRawは、そのオブジェクトのインスタンスポインターとその関数にリファレンスする関数がそのポインターを呼ぶ事を期待します。???

うーん。教科書のHow it works…もAPIよりは、沢山説明していますが、正直良く分からないです。29と30は、CreateRaw関数の前の説明ですし、31は文章の意味が不明です。まず、the object instanceとは何でしょうか?オブジェクトとインスタンスは基本的には同じ事です。オブジェクトインスタンスなんで言葉、私は、知りません。オブジェクトと仮定して、読んでみると、theがついていて、どのオブジェクトなの?となります。31の前の文章である、29と30を読んでも、このオブジェクトが指す単語は、見当たらないです。さらに、この文章を、expect sb to do stの構文と、考えるとinvokeが、doに当たるはずです。で、何が呼ばれるのかと、invokeの次を見ると、on that pointerと、なっています。Invoke は他動詞ですから、前置詞が次に来る事は、ないはずです。受動体ならinvokedなのですし、to be invokedになると思います。

では、Referenceを動詞と考えてこの文を見てみるとどうでしょうか?Some expect st, and others reference st else. と言う構文です。この構文が正しいならば三人称単数のa function がreferenceの主語になるためreferencesとならないとおかしいと思います。よってこの解釈は、おかしいと思います。

次に、createRawの最初のパラメーターである、thisを説明する個所が、a pointer to the object instance、2番目のパラメーターである、&UFU4CookbookEidtorModule::MyButton_Clickedを説明する個所が、a function reference to the function to invoke on the pointerと、考えてみます。となると、2番目のパラメーターである&UFU4CookbookEidtorModule::MyButton_Clickedは、referenceであるしfunctionでもあるので、a function referenceが&UFU4CookbookEidtorModule::MyButton_Clickedを指していると成り、かなりしっくりします。次にthe function to invoke on that pointerはa function referenceを修飾しているので、the function のtheは、to invoke on that pointerで修飾されているfunctionと考えられます。さらに、the function (whom) invoke on that pointerだとすれば、 invoke the function on that pointer となり、on that pointer が先の a pointerを指していると分かり、to invoke the function on that objectとなります。そのオブジェクトにある関数を呼ぶ、と解釈出来るようになり、大分意味が通るようになります。ではだれがこの関数を呼ぶのでしょうか? もうここはcreateRaw関数だと分かります。

後は、the object instanceの謎だけです。この単語は、最初のパラメーターのthisを指しているはずですが、ひょっとしたらthe object instanceとはthisの事を特別に指す言い方なのかもしれません。そう仮定するとtheの用法も明確になってきます。The moon とか、the United Statesなどの世界で一個だけある物を指す時のtheです。Thisは世界に一個しかないので、当然the object instanceとなり、an instance もしくは、an objectではなくなります。

31.CreateRawは(そのパラメーターに)ポインターと関数のリファレンスを要求します。そのポインターはオブジェクトインスタンス(this)を指し、その関数のレファレンスは先のポインターが示すオブジェクトインスタンス上の(createRaw関数に)呼ばれる関数を指します。

大分意訳ですが、前よりは大分意味が通ると思います。ただ、意味が通ってしまうと、教科書のHow it works…は「CreatRaw()は、パラメーターにオブジェクトを指すポインターと、そのオブジェクト上にある関数をさすレファレンスが必要だよ。」としか言ってなく、そんな事は、コードを見れば分かるよ。で感じです。

MapAction関数の最後のパラメーターは、UE4のAPIには

f:id:kazuhironagai77:20180225120933p:plain

と説明されています。この英文が読める人はいないと思います。もうまったく意味が取れません。Can this action be repeated if …? と。疑問文と解釈したら、いいえ、もうやめます。無理です。この文を解釈するのは。

では、教科書のHow it works…はどうでしょうか?

f:id:kazuhironagai77:20180225121011p:plain

32.MapActionの三番目のパラメーターは、このアクションが実行できるかどうかをテストするために呼ばれるデリゲートです。我々は、このコマンドがいつでも実行されたいので、単純かつ前もって定義された、常にtrueを返すデリゲートを使用します。

何か、APIと全然違う事が書かれています。VSのコード上のMapActionにマウスを重ねてみると、

f:id:kazuhironagai77:20180225121046p:plain

なんと、MapAction関数が4つのパラメーターをパスしています。4番目はオプショナルなので書いていなかったのです。微妙に違うAPIを読んでいました。こちらが正しいAPIでした。

だいたいですが、これでMapAction関数の意味が理解出来ました。

<今回のまとめ>

残念ながら時間がもうないので、今回はここまでで終わりにします。続きは次回やります。最後に今回検証した事をまとめます。

  • CookbookCommandsクラスがSlateモジュールの持つコマンドをC++側から使用出来るようにする事、UE4CookbookEditorクラスが、使用可能になったコマンドを用いてUE4エディターのツールボタンを作成しているという仮説は正しい。
  • UE4CookbookEditorクラス内のStartupModule()関数内で、呼ばれるFUICommandListクラスのオブジェクトが、MapAction関数を持っていて、そのMapAction関数が、CookbookCommandsクラスで作成したコマンド一式と、実際の機能を持つ関数を、デリゲートを用いてバインドさせる。
  • つまり、MapAction関数を理解する事がこのクラスを理解するために、更に言えばツールバーボタンを作成するために最も重要な事である。

MapAction関数について簡潔にまとめます。

f:id:kazuhironagai77:20180225121145p:plain

MapAction関数は、前回検証したFCookbookCommandsクラスのコマンド一式と、実際にそのコマンドが持つ事になる機能を(delegateを使って)マップ形式にバインドします。MapAction関数を使うためには、FUICommandsクラスのオブジェクトが必要です。MapAction関数はFUICommandsクラスのオブジェクトのメンバー関数であるからです。MapAction関数は、3つ(実際は4つ)のパラメーターを必要とします。最初のパラメーターは、FCookbookCommandsクラスのコマンド一式をパスします。次のパラメーターは、実際の機能を持つ関数をデリゲートとしてパスします。最後のパラメーターは、このアクションが実行できるかどうかをテストします。

<おまけ>

<クラス名::メンバー関数>

前回、この形式について、勉強し直すと宣言してしませんでしたので、今回します。学校で、この形式はシングルトンと習ったと思うのですが、まずシングルトンの定義を調べて見ました。

f:id:kazuhironagai77:20180225121300p:plain

上記の定義は、Wikipediaからです。一つのクラスが、一つのオブジェクトしか作れないと書いてあります。クラス名::メンバー関数と言う様式が、シングルトンと呼ばれるのは、正しいようです。

次に、C++のシングルトンにおけるメンバー関数の呼び出しについて調べて見ると、Correct way to call a singleton object in C++に以下のように書いてます。

f:id:kazuhironagai77:20180225121327p:plain

これで作ればいいのかと思ったら、ここは、質問と回答で、教科書的にどう作ればいいのかと、何故そう作らないといけないのかが、書かれたサイトではなかったです。まず。ネットでC++の教科書的なサイトの中からシングルトンを探そうとサーチしたら、見つからないです。ので、これも次回に延期します。