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

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

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

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

Q&A

解決済

4回答

1470閲覧

C++ : 継承の使用方法でわからないところがあります。

UE4benkyo-

総合スコア37

C++

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

0グッド

1クリップ

投稿2021/05/14 04:30

編集2021/05/15 13:53

継承でわからないことがあります。
よろしければアドバイスもらえませんか?

Objectという基底クラスがあります。
中身は
初期化関数
更新関数
描画関数

メンバ変数は
座標、
スケール、
当たり判定用のコリジョンクラスです。

描画関数は全部同じ処理なので
オーバーライドさせず基底クラスに定義を書いています。

今回継承させようとしているのは
プレイヤー
エネミー
ボックス(四角い箱です)

それぞれ当たり判定があり

プレイヤーとエネミーは弾を打ちます。

ボックスは弾が当たったら消えるようになっています。

ですがボックスは弾を打ちません。
基底クラスにBulletクラスを持たせてもいいと思いますか?

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

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

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

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

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

episteme

2021/05/14 04:47

> オーバーライドでない派生クラスの個別の関数って どうやって呼び出せますか? それを呼び出したいシチュエーションを例示してください。
guest

回答4

0

ベストアンサー

質問の意図ですが、下記のようなもので「オーバーライドされていないfuncA()を呼び出す」ということで間違いないでしょうか?
その通りであれば、下記のコードに書いたとおりに基底クラスの関数名を書けばいいです。

回答を受けてコンストラクタとメンバーを追加しました。

c++

1class A 2{ 3public: 4 int memA; 5 A(const A &a) { 6 this.memA = a.memA; 7 } 8 void funcA() { 9 printf("A¥n"); 10 } 11} 12 13class B: class A 14{ 15public: 16 B(const A &a) 17 : A(a) 18 {} 19 20 void funcB() { 21 printf("B¥n"); 22 } 23} 24 25int main() { 26 B objB = new B(); 27 objB.funcA(); 28 if (objB) 29 { 30 delete objB; 31 } 32 return 0; 33}

質問内容の変更に伴い、コードを変更してみました。

ラフなコードを書いているので、細かいところで間違いがあると思いますがご容赦を。

c++

1//オブジェクト種別 2enum { 3 ePlayer = 0, 4 eEnemy, 5 eBox, 6 eMax, 7}; 8 9class Object 10{ 11public: 12 (注釈:本来はprivateにすべきだが、簡単にするためにpublicにしている) 13 int 座標; 14 int スケール; 15 int 当たり判定用のコリジョンクラス; 16 int オブジェクト種別; 17 18 Object (const Object& obj) { 19 座標 = obj.座標; 20 スケール = obj.スケール; 21 当たり判定用のコリジョンクラス = obj.当たり判定用のコリジョンクラス; 22 オブジェクト種別 = obj.オブジェクト種別; 23 } 24 public 初期化関数; 25 public 更新関数; 26 public 描画関数; 27} 28 29class Character: class Object 30{ 31public: 32 Bulletクラス; 33 34 Character (const Character& character) 35 : Object(character) 36 {} 37} 38 39class Player: class Character 40{ 41public: 42 Player() 43 : Character(), 44 オブジェクト種別 = ePlayer 45 {} 46 47 Player(const Player& player) 48 : オブジェクト種別 = ePlayer, 49 Character(player) 50 {} 51} 52 53class Enemy: class Character 54{ 55 Enemy() 56 : Character(), 57 オブジェクト種別 = eEnemy 58 {} 59 60 Enemy(const Enemy& enemy) 61 : オブジェクト種別 = eEnemy, 62 Character(enemy) 63 {} 64} 65 66class Box: class Object 67{ 68 Box() 69 : Object(), 70 オブジェクト種別 = eBox 71 {} 72 73 Box(const Box& box) 74 : オブジェクト種別 = eBox, 75 Character(box) 76 {} 77} 78 79int main(void) 80{ 81 Player *player = new player(); 82 Enemy *enemy = new Enemy(); 83 Box *box = new Box(); 84 Object* objArray[] = {player, enemy, box}; 85 86 for (int i = 0; i < 3; i++) { 87 switch (objArray[i].オブジェクト種別) { 88 case ePlayer: 89 (dynamic_cast<Player*>objArray[i])->(Playerクラス専用関数); 90 break; 91 case eEnemy: 92 (dynamic_cast<Enemy*>objArray[i])->(Enemyクラス専用関数); 93 break; 94 case eBox: 95 (dynamic_cast< Box*>objArray[i])->(Boxクラス専用関数); 96 break; 97 } 98 } 99}

投稿2021/05/14 08:20

編集2021/05/15 17:40
rinjinto

総合スコア170

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

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

UE4benkyo-

2021/05/14 11:51

回答ありがとうございます! ですがちょっと私の聞きたいことではないです... これは私が悪かったです。 勘違いさせてしまい申し訳ありません。 私が聞きたいのは、派生クラスにしかないオリジナルの関数をどうやって呼び出せばいいのか?ということです。
yumetodo

2021/05/14 11:53

この例で言うなら、funcBを呼び出すんでだめなので?
UE4benkyo-

2021/05/14 11:57

基底クラス型配列にまとめてやっています。で私がやると基底クラスの関数しか出てこないんです。 なので何か方法はありますか?
maisumakun

2021/05/14 12:00

> 基底クラス型配列にまとめてやっています。で私がやると基底クラスの関数しか出てこないんです。 なぜその状況で派生クラスのメソッドを呼びたいのですか?(配列に、基底クラスあるいは別の派生クラスのインスタンスが入っていた場合、本当に呼べません)
UE4benkyo-

2021/05/14 12:08 編集

ありがとうございます。 基底クラスは共通の処理をもたせています(初期化、更新 描画みたいな)。 派生クラスは共通の処理の描画以外はオーバーライドしています。 配列にする理由はmainとかでforで回せばいいからです。あときれいに見えます。 呼び出せないのって当たり前ですか?
maisumakun

2021/05/14 12:10

> 配列にする理由はmainとかでforで回せばいいからです。あときれいに見えます。 派生クラスの配列にする、ではだめなのですか?
maisumakun

2021/05/14 12:11

> 呼び出せないのって当たり前ですか? はい、基底クラスの型のままで派生クラスにしかないメソッドを呼ぶ方法は(言語内では)ありません。
UE4benkyo-

2021/05/14 12:16

ありがとうございます。 なるほど... 派生クラスの配列より基底クラスの配列の方が見た目がきれいに見えませんか? 派生クラスの関数も呼び出すことができないんですね... 私がまだ継承のことを完全に理解していないのがわかりました。
maisumakun

2021/05/14 12:24 編集

> 派生クラスの配列より基底クラスの配列の方が見た目がきれいに見えませんか? そういう問題だったのですか…(ずっこけ) コードは正しく動くのがまず前提です。「正しく動かないけど見た目がきれい(だと思っている)コード」には、プログラムコードとしての価値はありません。キャストすれば動くには動きますが、コードの見栄えは致命的に悪くなります。
UE4benkyo-

2021/05/14 12:28

わかりました。 ありがとうございます。 では結論として 派生クラスの個別の関数は呼び出す方法がないということですね? そして、個別の関数を呼び出すにはそれぞれ生成するしかないということでOKですか?
maisumakun

2021/05/14 12:30

> 派生クラスの個別の関数は呼び出す方法がないということですね? いちおう、キャストすれば呼べます(が、できれば避けるべき場面です)。 > 個別の関数を呼び出すにはそれぞれ生成するしかないということでOKですか? 「それぞれ生成する」とはどういう意味でしょうか?
yumetodo

2021/05/14 12:51

もうちょっと全体の設計を教えてもらえればもっとマシな設計を回答できると思うんですよね。こういう質問が出るってことは初手から間違えているパターンです。
UE4benkyo-

2021/05/14 13:46

yumetodoさんアドバイスありがとうございます! 次はうまく説明できるように努力します。
UE4benkyo-

2021/05/14 13:47

maisumakunさん 配列にしないでメンバとして別々に実体化させるという感じです。
maisumakun

2021/05/14 13:49

えっと、「派生クラスの配列」ではだめなのですか?
rinjinto

2021/05/14 14:00

回答を受けてコードを若干変更しました。 上記のようにコピーコンストラクタを作成して、基底クラスオブジェクト(ここではaと記述)を取得後、 B b = new B(a); という形でBクラスのオブジェクトにする。 その後、 b.funcB(); という形で派生クラスのメソッドを呼び出す。 こんな感じでいかがでしょうか?
rinjinto

2021/05/14 14:06

基本的に継承先の関数というのは基底クラスからは呼び出せません。 ですので、基底クラスオブジェクトを1回派生クラスオブジェクトに組み込んでから派生クラスオブジェクトを呼び出す。 という手順が必要になります。
UE4benkyo-

2021/05/15 13:08

返信遅くなりました。すみません。 maisumakunさん 派生クラスの配列だと他の派生クラスの個別の関数が呼べなくないですか?
maisumakun

2021/05/15 13:13

> 派生クラスの配列だと他の派生クラスの個別の関数が呼べなくないですか? えっと、バラバラな派生クラスを1つに詰め込むのですか?(1つだけだと考えていました)
yumetodo

2021/05/15 13:15

どうみてももともとの設計が狂ってやがる・・・。
UE4benkyo-

2021/05/15 13:16

maisumakunさん 一応そのつもりです。
UE4benkyo-

2021/05/15 13:18

yumetodoさん そうなんですかね... 共通の関数は出来ますけど(初期化関数、描画関数、更新関数など) やっぱり個別のゲッターとかないと困りませんか?
UE4benkyo-

2021/05/15 13:21 編集

それとも共通でないメンバや関数の引数も少しなら入れてもいいのですかね? 少しでもだめですかね?
maisumakun

2021/05/15 13:27

なぜ共通でないものを共通に扱いたいのか、そこを考える必要があります。
maisumakun

2021/05/15 13:31

> それとも共通でないメンバや関数の引数も少しなら入れてもいいのですかね? 基底クラスとして扱う場面で呼ばないなら、メソッドやフィールドを拡張しようが一向に構いません。
yumetodo

2021/05/15 13:31

共通ではないものを共通に扱おうとする、人これを矛盾といふなり。 だからそもそもの設計が狂ってるから全体像を提示してみてはと言ってるわけです。そもそも継承がいらなかったというオチもあるので。
UE4benkyo-

2021/05/15 13:35

yumetodoさん 確かにそうですね。わかりました。 ちょっと質問のところに書きますね。
rinjinto

2021/05/15 17:43 編集

質問内容の変更に伴い、実装を変更したものを追記しました。 このように3つともObjectクラスを継承するのではなく、 プレイヤーとエネミー用にさらに継承するクラス(Character)を作成して、そこにBulletクラスオブジェクトを設定、プレイヤーとエネミークラスはCharcterクラスを継承、ボックスクラスはObjectクラスを継承すると、ボックスクラスにBulletクラスオブジェクトを持たせることなく実装できます。
rinjinto

2021/05/15 16:06

これらを1つのObjectオブジェクトのポインタ配列(実体配列ではないことに注意!)に持たせて、使う際にそのオブジェクトのオブジェクト種別を判断してキャストをすれば、派生クラスのみの関数を使用することができます。
rinjinto

2021/05/15 16:30 編集

おそらく1フレームごとに全部のオブジェクトの描画処理を行うためにこんな配列を作っているのかと思いますので、それであれば1つの配列にまとめるのはありなのでは、と自分は思います。 まあ、3つとも別の配列を作って、それぞれをforループで回したほうが読みやすい気もしますが。(処理は遅くなりますが)
UE4benkyo-

2021/05/16 04:44

rinjintoさんありがとうございます! たしかにこの考え方だとできそうです。 私がまだまだ継承の考え方を理解していないことがわかりました。
guest

0

###案1 キャストする。
キャストすることで派生クラスとして利用する。

###案2 オーバーライド関数を窓口にする。
オーバーライド関数を経由して派生クラス独自関数を呼び出す。

投稿2021/05/15 13:33

HogeAnimalLover

総合スコア4830

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

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

0

こーゆーことがやりたいのかな?

C++

1#include <iostream> 2#include <vector> 3#include <memory> 4 5class Base { 6public: 7 virtual ~Base() {} 8 void base_fn() { std::cout << "Base::base_fn\n"; } 9}; 10 11class Derived : public Base { 12public: 13 ~Derived() override {} 14 void derived_fn() { std::cout << "Derived::derived_fn\n"; } 15}; 16 17int main() { 18 std::vector<std::unique_ptr<Base>> vec; 19 vec.push_back(std::make_unique<Base>()); 20 vec.push_back(std::make_unique<Derived>()); 21 vec.push_back(std::make_unique<Base>()); 22 vec.push_back(std::make_unique<Derived>()); 23 24 for ( auto& item : vec ) { 25 Derived* der = dynamic_cast<Derived*>(item.get()); 26 if ( der ) { der->derived_fn(); } 27 else { item->base_fn(); } 28 } 29}

投稿2021/05/14 12:22

編集2021/05/14 12:32
episteme

総合スコア16614

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

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

0

ですけどmain で処理を呼ぶときに

オーバーライドでない派生クラスの個別の関数って
どうやって呼び出せますか?

おそらく、何かしら設計を誤っています。

「派生クラスにしかない機能を使いたい」のであれば派生クラスの変数に入れるべきですし、基底クラスで参照するなら基底クラスにあるメソッドだけを使うべきです。

投稿2021/05/14 11:58

maisumakun

総合スコア145208

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

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

maisumakun

2021/05/14 12:04

どうしても呼びたい場合はキャストも使えますが、それよりは設計に問題がないかを考えるのが先決です。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.46%

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

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

質問する

関連した質問