ページ 11

C++  継承?についての質問

Posted: 2018年8月30日(木) 19:21
by 神田空太
私は最近クラスの継承を使い始めて、おそらくとても基礎的な段階なので、失礼も多いかと思います。
しかし、こう書くべき、こう考えるべき、といった意見があればぜひご教授頂きたいです。
そして、本当に見ずらいと思います。本当に申し訳ありません。。。

(VisualStudioでマリオのような2Dアクションゲームの作製をしようとしています。)
そして、継承に関するエラーコードが出たのですが、その原因が分からず苦戦しています。
エラー対象だと思われるコードを質問の下に書きました。そちらを先に見ていただくとありがたいです。


まず、出ているエラーは

'Roll_Char_Parameter_IsAbleGo':定義されていない基底クラスが宣言されています。
(プロジェクト:Action_Sample、ファイル:roll_char_parameter.h、行:13)

'Roll_Char_Parameter':定義されていない基底クラスが宣言されています。
(プロジェクト:Action_Sample、ファイル:roll_char_parameter_isable.h、行:8)

の二つです。この文章によると、基底クラスのRoll_Char_Parameter_ShouldGoクラスが定義されてないの事ですが、
きっちり継承させるクラスのヘッダにはRoll_Char_Parameter_ShouldGoのヘッダファイルをインクルードさせています。
Roll_Char_Parameter_IsAbleGoとRoll_Char_Parameterから見ても、Roll_Char_Parameter_ShouldGoは
インクルードされているので、定義はされていると思うのですが、このエラーが出てしまいます。

////////////////////////ここからソースコード/////////////////////////////

エラー対象だと思われるクラスは3つ。
・Roll_Char_Parameter(Roll_Char_Parameter_IsAbleGo、Roll_Char_Parameter_ShouldGoを基底とする)
これの8行目(//☆☆☆☆エラー☆☆☆☆)がエラー
・Roll_Char_Parameter_IsAbleGo(Roll_Char_Parameter_ShouldGoを基底とし、Roll_Char_Parmeterの基底である)
これの13行目(//☆☆☆☆エラー☆☆☆☆)がエラー
・Roll_Char_Parameter_ShouldGo(Roll_Char_Parameter、Roll_Char_Parameter_IsAbleGoの基底)

//継承関係

Parameter_ShouldGo
         ↑
Parameter_IsAbleGo Parameter_ShouldGo
↓ ↑ ↑
| L---------------------------------」
| |
L--------------→Parameter


Roll_Char_Parameter

コード:

***********************************************************************
*****************Roll_Char_Parameter.h**************************
***********************************************************************
#pragma once
#include "Roll_Char_Parameter_IsAbleGo.h"
#include "Roll_Char_Input.h"
#include "Roll_Char_Parameter_ShouldGo.h"
#include "Interface_RollChar_GetParamater.h"
#include "Interface_Stage_GetParameter.h"

 class Roll_Char_Parameter : virtual private Roll_Char_Input
	 , public Interface_RollChar_GetParameter
	 , virtual private Roll_Char_Parameter_IsAbleGo
	 , virtual private Roll_Char_Parameter_ShouldGo //☆☆☆☆エラー☆☆☆☆

{

private:
	int X = 0;
	int Y = 0;
	int Hp = 0;
	int Speed = 20;
	int Gravity = 5;
	int Junp = 0;
	
	bool DrawFlg = false;
	bool IsAbleGo1 = true;
	bool IsAbleGo2 = true;
	
	Direct ShouldGo1 = None;
	Direct ShouldGo2 = None;
	
protected:
	Interface_Stage_GetParameter * Stage_GetParameter = NULL;

public:
	Roll_Char_Parameter();
	~Roll_Char_Parameter();

	void Update();
	void Draw();

	void SetPoint(int x, int y) { X = x, Y = y; }
	void SetSpeed(int speed) { Speed = speed; }

	int GetX() override { return X; }
	int GetY() override { return Y; }
	int GetHp() override { return Hp; }
	int GetSpeed() { return Speed; }

	void SetDrawFlg(bool flg) { DrawFlg = flg; }
	void SetGravity(int gravi) { Gravity = gravi; }

};

***********************************************************************
***************Roll_Char_Parameter.cpp*************************
***********************************************************************
#include "Roll_Char_Parameter.h"


Roll_Char_Parameter::Roll_Char_Parameter()
{
}

Roll_Char_Parameter::~Roll_Char_Parameter()
{
}

void Roll_Char_Parameter::Update()
{

	Roll_Char_Parameter_IsAbleGo::Update();

	IsAbleGo1 = Roll_Char_Parameter_IsAbleGo::GetIsAbleGo1();
	IsAbleGo2 = Roll_Char_Parameter_IsAbleGo::GetIsAbleGo2();
	ShouldGo1 = Roll_Char_Parameter_ShouldGo::GetShouldGo1();
	ShouldGo2 = Roll_Char_Parameter_ShouldGo::GetShouldGo2();

	if(IsAbleGo1 && IsAbleGo2)					Y += Gravity;
	else if (ShouldGo1 != U && ShouldGo2 != U)	Y += Gravity;

	if (IsAbleGo1) {
		if (Roll_Char_Input::GetUp() == 1)		Y -= Speed;
		if (Roll_Char_Input::GetDown() == 1) 	Y += Speed;
		if (Roll_Char_Input::GetRight() == 1)	X += Speed;
		if (Roll_Char_Input::GetLeft() == 1) 	X -= Speed;
	}
	else {
		if (Roll_Char_Input::GetUp() == 1 && (ShouldGo1 != D) && (ShouldGo2 != D))		Y -= Speed;
		if (Roll_Char_Input::GetDown() == 1 && (ShouldGo1 != U) && (ShouldGo2 != U)) 	Y += Speed;
		if (Roll_Char_Input::GetRight() == 1 && (ShouldGo1 != L) && (ShouldGo2 != L))	X += Speed;
		if (Roll_Char_Input::GetLeft() == 1 && (ShouldGo1 != R) && (ShouldGo2 != R)) 	X -= Speed;
	}
}

void Roll_Char_Parameter::Draw()
{
	if (DrawFlg) {
		DrawFormatString(20, 40, GetColor(255, 255, 255), "X : %d, Y : %d", X, Y);
		DrawFormatString(20, 60, GetColor(255, 255, 255), "ShouldGo : %d", ShouldGo1);
		DrawFormatString(20, 80, GetColor(255, 255, 255), "IsAbleGo : %d", IsAbleGo1);
		DrawFormatString(20, 100, GetColor(255, 255, 255), "StageWidth : %d, StageHeight : %d", Stage_GetParameter->GetWidth(), Stage_GetParameter->GetHeight());
	}
}

Roll_Char_Parameter_IsAbleGoクラス

コード:

***********************************************************************
***********Roll_Char_Parameter_IsAbleGo.h********************
***********************************************************************

#pragma once

#include "Roll_Char_Parameter.h"
#include "Roll_Char_Parameter_ShouldGo.h"

class Roll_Char_Parameter_IsAbleGo : 
	virtual private Roll_Char_Parameter
	,virtual private Roll_Char_Parameter_ShouldGo //☆☆☆☆エラー☆☆☆☆
{

	bool IsAbleGo1 = true;
	bool IsAbleGo2 = true;
	int X, Y, Speed;

public:
	Roll_Char_Parameter_IsAbleGo();
	~Roll_Char_Parameter_IsAbleGo();

	void Update();

	bool GetIsAbleGo1() { return IsAbleGo1; }
	bool GetIsAbleGo2() { return IsAbleGo2; }

};

***********************************************************************
**************Roll_Char_Parameter_IsAbleGo.cpp**************
***********************************************************************
#include "Roll_Char_Parameter_IsAbleGo.h"

Roll_Char_Parameter_IsAbleGo::Roll_Char_Parameter_IsAbleGo()
{
}

Roll_Char_Parameter_IsAbleGo::~Roll_Char_Parameter_IsAbleGo()
{
}

void Roll_Char_Parameter_IsAbleGo::Update()
{

	if (X + Speed > (Stage_GetParameter->GetX() + Stage_GetParameter->GetWidth()))			IsAbleGo1 = false, Roll_Char_Parameter_ShouldGo::SetShouldGo1(L);
	else if (X - Speed < Stage_GetParameter->GetX())										IsAbleGo1 = false, Roll_Char_Parameter_ShouldGo::SetShouldGo1(R);
	else if (Y + Speed >(Stage_GetParameter->GetY() + Stage_GetParameter->GetHeight()))	IsAbleGo1 = false, Roll_Char_Parameter_ShouldGo::SetShouldGo1(U);
	else if (Y - Speed < Stage_GetParameter->GetY())										IsAbleGo1 = false, Roll_Char_Parameter_ShouldGo::SetShouldGo1(D);
	else																					IsAbleGo1 = true, Roll_Char_Parameter_ShouldGo::SetShouldGo1(None);

	if (X + Speed >(Stage_GetParameter->GetX() + Stage_GetParameter->GetWidth()) && Roll_Char_Parameter_ShouldGo::GetShouldGo1() != L)			IsAbleGo2 = false, Roll_Char_Parameter_ShouldGo::SetShouldGo2(L);
	else if (X - Speed < Stage_GetParameter->GetX() && Roll_Char_Parameter_ShouldGo::GetShouldGo1() != R)										IsAbleGo2 = false, Roll_Char_Parameter_ShouldGo::SetShouldGo1(R);
	else if (Y + Speed >(Stage_GetParameter->GetY() + Stage_GetParameter->GetHeight()) && Roll_Char_Parameter_ShouldGo::GetShouldGo1() != U)	IsAbleGo2 = false, Roll_Char_Parameter_ShouldGo::SetShouldGo1(U);
	else if (Y - Speed < Stage_GetParameter->GetY() && Roll_Char_Parameter_ShouldGo::GetShouldGo1() != D)										IsAbleGo2 = false, Roll_Char_Parameter_ShouldGo::SetShouldGo1(D);
	else																					IsAbleGo2 = true, Roll_Char_Parameter_ShouldGo::SetShouldGo2(None);
}

Roll_Char_Parameter_ShouldGoクラス

コード:


***********************************************************************
**************Roll_Char_Parameter_ShouldGo.h****************
***********************************************************************

#pragma once

typedef enum
{
	R,
	L,
	U,
	D,
	None
}Direct;

class Roll_Char_Parameter_ShouldGo
{

	Direct ShouldGo1 = None;
	Direct ShouldGo2 = None;

public:
	Roll_Char_Parameter_ShouldGo();
	~Roll_Char_Parameter_ShouldGo();

	void SetShouldGo1(Direct Should) { ShouldGo1 = Should; }
	void SetShouldGo2(Direct Should) { ShouldGo2 = Should; }

	Direct GetShouldGo1() { return ShouldGo1; }
	Direct GetShouldGo2() { return ShouldGo2; }

};

***********************************************************************
**************Roll_Char_Parameter_ShouldGo.cpp*************
***********************************************************************

#include "Roll_Char_Parameter_ShouldGo.h"

Roll_Char_Parameter_ShouldGo::Roll_Char_Parameter_ShouldGo()
{
}

Roll_Char_Parameter_ShouldGo::~Roll_Char_Parameter_ShouldGo()
{
}


Re: C++  継承?についての質問

Posted: 2018年8月30日(木) 19:34
by 神田空太
訂正です

・Roll_Char_Parameter_IsAbleGo

(Roll_Char_Parameter_ShouldGo
「とRoll_Char_Parameter」
を基底とし、Roll_Char_Parmeterの基底である)

//継承関係(非常に見ずらいです。。「.」が空白のかわりです

Parameter_ShouldGo
...........↑
Parameter_IsAbleGo...........Parameter_ShouldGo
....↓.....↑.................................↑
....|.....Lーーーーーーーーーーーー」
....|....................↑
....Lーーーーー→Parameter

Re: C++  継承?についての質問

Posted: 2018年8月30日(木) 22:34
by 参照魚
2つのクラスが互いの基底クラス(class A : Bと class B : A)になって、
構造が循環してしまっているせいだと思います。

コード:

// Roll_Char_Parameter_IsAbleGo.h
class Roll_Char_Parameter_IsAbleGo : virtual private Roll_Char_Parameter

// Roll_Char_Parameter.h
class Roll_Char_Parameter : virtual private Roll_Char_Parameter_IsAbleGo
virtual 継承は共通の基底クラスを持つクラスを多重継承した場合に基底クラスを1つにまとめる(ダイアモンド継承にする)場合で、この循環する構造の回避はできないと思います。

Re: C++  継承?についての質問

Posted: 2018年8月30日(木) 22:54
by 神田空太
解決しました。

まず、読みずらい中の解答、本当にありがとうございます。助かりますm(_ _)m

クラスの継承で、お互いが基底クラスになってしまってはいけないのですね。
関係を

Parameter_ShouldGo
...........↑
Parameter_IsAbleGo...........Parameter_ShouldGo
..........↑.................................↑
..........Lーーーーーーーーーーーー」
.........................↑
....................Parameter

にしましたら普通にできました。

Parameterにあるインターフェースを使いたく、循環の形を取りましたが、
IsAbleGoにもう一つインターフェースを宣言する事でうまくいきました。

下位の階層にあるインターフェースは使えない事を今回知ることができてとてもうれしいです。
理解が進みます!

これからもよろしくお願いします!!!

Re: C++  継承?についての質問

Posted: 2018年8月30日(木) 23:15
by 参照魚
Parameterクラスをpublicで継承すれば派生クラスからでもParameterクラスのメンバ関数を呼び出せます。

Re: C++  継承?についての質問

Posted: 2018年8月31日(金) 00:01
by 神田空太
なるほど、あくまでpublicは継承した、されたクラスで使用可能なのですね。
てっきり階層が低いところからは呼び出せないかと思っていました。

すごく勉強になります。的確です!

Re: C++  継承?についての質問

Posted: 2018年8月31日(金) 00:12
by 参照魚
Parameterクラスが基底クラスで、Parameter_ShouldGoクラスが派生クラスであってますでしょうか?
それなら呼び出せますが逆だったら呼び出せません(すいません)

Re: C++  継承?についての質問

Posted: 2018年8月31日(金) 07:34
by 神田空太
Parameter_IsAbleGo(↓のInterfaceは使えない)
......↑
Parameter(Interfaceがある:publicにする)

継承関係は上のとおりです。先ほど、この継承関係で、Parameterの使いたいメンバ(インターフェース)をpublic
にして試してみました。ご忠告のとおり出来ませんでした。
やはり下の階層には共有されますが、上の階層には共有されないですね。

確認せず返信してしまって申し訳ありません。

また、今回の設計では、なるべく上の階層にインターフェースを置くべきですね。

Re: C++  継承?についての質問

Posted: 2018年8月31日(金) 21:26
by 参照魚
Parameter_IsAbleGoはGo可能かどうか判定する関数とその結果を保持する変数だけのクラスにした方がよいかと思います。
Parameter_IsAbleGoにメンバ変数にX,Y,Speedは持たせず。必要なパラメーターはUpdateの引数で受け取るのではどうでしょうか?

コード:

void Roll_Char_Parameter::Update()
{
	Roll_Char_Parameter_IsAbleGo::Update( this, Stage_GetParameter );
}

Re: C++  継承?についての質問

Posted: 2018年9月01日(土) 12:57
by 神田空田
僕の中でUpdate関数は引数なしで書く癖
があったのでああいう風に書いてしまいました。

ちゃんと何を持つべきか考えるべきですね。

ご教授、本当にありがとうございます。

Re: C++  継承?についての質問

Posted: 2018年9月01日(土) 12:57
by 神田空田
僕の中でUpdate関数は引数なしで書く癖
があったのでああいう風に書いてしまいました。

ちゃんと何を持つべきか考えるべきですね。

ご教授、本当にありがとうございます。