質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

1回答

4782閲覧

基底クラスメンバ関数から派生クラスのインスタンスを生成したい

mushroom314

総合スコア29

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2017/07/28 18:58

お世話になっております。独学でゲームを開発しています。
シーケンス推移の実装で困っています。

##前提
インターフェイスとして機能する抽象クラス(StageBaseクラス)を派生した様々なGameStageクラスを作り、それのnew/deleteでステージ推移を管理しようとしています。

enumでゲームステージを区別し、std::mapを用いてenumでゲームステージクラスのポインタを呼び出し適宜生成しようと考え、以下のようなクラスを定義しました。

c++

1class StageBase{//GameStageの基底 2 virtual void draw() = 0; 3 virtual void update() = 0; 4 static StageBase* build(){ 5 StageBase* sb = new StageBase(); 6 return sb; 7 } 8 //... 9}; 10class GameStage1 : public StageBase{ 11 void draw(){ 12 //... 13 } 14 void update(){ 15 //... 16 } 17}; 18class GameStage2 : public StageBase{ 19 //GameStage1と同様、ステージ分クラスがある 20}; 21 22class StageManager{ 23 typedef enum stageName{ 24 TITLE, 25 STAGE01, 26 //... 27 } StageName; 28 29 std::map<StageName, StageBase*> mp; 30 31 void goToNextStage(StageName sn){ 32 mp.at(sn)->build();//問題の箇所 33 } 34};

##問題
まず純粋仮想関数がStageBaseに存在するとインスタンス化できないためコンパイル時にgoToNextStage関数でエラーが出ます。
しかし=0を取り除いてインスタンス化できるようにすると、

`vtable for SceneBase' に対する定義されていない参照です

などと出てリンカエラーになります。
##質問
そもそも、newしたいクラスをコンテナからうまく取り出してインスタンス化するというのがあまり良くない考えなのでしょうか?
if文やswitch文で、全GameStage分のnewするコードを書けば解決するとは思うのですが、ストラテジーパターンのように、うまく切り替えながら同じインターフェイスでnewできればすっきりすると思うのです。
なにとぞお知恵を貸してください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yohhoy

2017/08/02 02:46

ファクトリメソッド(Factory Method)・パターンがこの目的に適合するのではないでしょうか。
mushroom314

2017/08/02 13:35

ご回答ありがとうございます。ファクトリメソッド的なことを実現したいのですが、やはり別の派生クラスを生成するためにはif文やswitch文でnewするものを振り分ける必要があると思います。そこを横着したいと考え試行錯誤していましたが、おそらくmapに要素を追加するコードを書くのと手間はあまり変わらないのではと考えるようになりました。ありがとうございます。
guest

回答1

0

ベストアンサー

クラスの派生系統についてのお考えがよくわかりませんが、少なくとも以下の場所はおかしいです。

・build関数はstaticであるため、インスタンスを経由する必要はないはず。
・SceneBaseという、ソースコード上に存在しない語がエラーメッセージに現れている。

投稿2017/07/29 02:54

HogeAnimalLover

総合スコア4830

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mushroom314

2017/07/30 02:51

コメントありがとうございます。混乱してタイプミスしていて、StageBaseの間違いです。やりたいことは、ゲームステージの(初期化していない)ポインタのリストを用意して、ステージロード時に、読み込みたいステージの(基底クラスの)ポインタからbuild()で(継承クラスの)インスタンスを実体化したいということで、初期化しないためポインタからstatic関数を呼び出そうと考えました。
HogeAnimalLover

2017/07/31 12:58

まず第一にstatic関数の呼び出しに実体もポインタも不要です。 そして、「基底クラスのメソッドの中で派生クラスのインスタンスを実体化」というのはよくわかりません。 多分、以下のことをしたいのだと思いますが・・・あってますか・・・? ・基底クラスにおいて、build関数を(staticでない)仮想関数にする。 ・各派生クラスにおいて、build関数を再定義する(自身をnewして、派生クラスのポインタとして返す)
mushroom314

2017/08/01 15:55

ご回答ありがとうございます。staticに関しては用法を誤解しておりました。ありがとうございます。 実現したいことはおっしゃる通りです。仮想関数を使うことで同じコードで色々な派生クラスの「build」をできるようにしたいのですが、そもそも関数を呼ぶためには実体かポインタが必要に思います。リソースを節約するためにbuildするまでは(呼び出し元である)実体を生成したくないと思い、ならばstatic関数にしていきなり実体を生成するようにしようかと思い至ったのですが、そうするとどの派生クラスのインスタンスを生成するのかという情報を(ポリモーフィックには)受け渡せないと思いました。「(派生クラス名)::build()」の派生クラス名だけを何らかの方法で色々指定することはできますでしょうか…? まず設計段階での考え方に無理がある箇所がありましたら、ご指摘いただければ幸いです。
HogeAnimalLover

2017/08/02 11:52

だとすると 「インスタンス生成するのみのメソッドをポリモーフィックに記述する」ということになると思います。が、この必要性がよくわかりません。基本的には単純に「new ○○」と書けば、外部でインスタンス生成をすることができるはずです。付随する初期化処理を書きたいならばコンストラクタを使えば解決します。また、○○の部分を条件ごとに書けばいいので、クラスの派生系統関係なしに目的は果たせるはずです。これを「ポリモーフィックに解決する」という必要性がわかりません。 ちなみに、「基本的に外部からでもnewは可能」と書きましたが、これが不可能となる例外もあります。これはコンストラクタがprivateやprotectedメソッドの場合です。この代表例はシングルトンと呼ばれるものです。これ以外でも、インスタンス生成を制限する場合は、「自らのstaticメソッド内でのみ自身のnewを許す」ような記述例はありえます。が、そうでない限りご提示の記述になるようなプログラムの用途がわかりません。
mushroom314

2017/08/02 13:40

ご回答ありがとうございます。確かに改めて必要かどうか考えてみますと、もともとは条件分岐を書く手間をポリモーフィズムで吸収できないかと考えていましたが、仮に私の意図するものが実現できたとしても、コンテナに追加するコードを書く等あまり手間が変わらないと気づきました。 素直に条件分岐してnewするようにします。 考えが整理できましたので、解決とさせていただきます。これまでご丁寧に付き合っていただきましてありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問