ページ 11

C++における、抽象クラスについて

Posted: 2014年5月06日(火) 12:50
by Bone
短期間に2度も質問してしまいますが、C++の仮想クラスにおける、その仮想クラスの子クラスに仮想クラスに含まれていないメソッドをどうよびだせばいいですか?

コード:

class ITest{
public:
 virtual void Updata() = 0;
 virtual ~ITest(){}
};

class CTest:public ITest{
public:
 virtual Updata(){}override;
 Draw();
};

int main(){
 ITest *ptr;
 ptr = new CTest();
 ptr->Updata();
 (CTest*)ptr->Draw(); /*エラーがでる。描画関数を呼び出したい*/
}
この場合、VCを使ってるのですが、エラーが出ます。
これを実現させる方法はあるのでしょうか?
開発環境は、VC++2010、Windowsです。

Re: C++における、抽象クラスについて

Posted: 2014年5月06日(火) 13:02
by みけCAT
このように書き換えると、Ideone.comのC++ 4.8.1でコンパイルを通すことができました。

コード:

#define override

class ITest{
public:
 virtual void Updata() = 0;
 virtual ~ITest(){}
};
 
class CTest:public ITest{
public:
 virtual void Updata(){}override;
 void Draw();
};
 
int main(){
 ITest *ptr;
 ptr = new CTest();
 ptr->Updata();
 ((CTest*)ptr)->Draw(); /*エラーがでる。描画関数を呼び出したい*/
}

void CTest::Draw() {}

Re: C++における、抽象クラスについて

Posted: 2014年5月06日(火) 13:25
by usao
オフトピック
ダウンキャストしないといけない場面なのでしょうか

Re: C++における、抽象クラスについて

Posted: 2014年5月06日(火) 13:43
by softya(ソフト屋)
ダウンキャスト(親→子)は本来違う子をキャストしてしまう可能性があり危険なので出来るだけ避けるのが鉄則です。
なので、ダウンキャストが起こる設計自体を見なおしたほうが良いですね。

※ せっかくの抽象化を無意味にしてしまうので、オブジェクト指向的にも良くないと思います。

Re: C++における、抽象クラスについて

Posted: 2014年5月06日(火) 14:46
by Bone
みけCAT さんが書きました:このように書き換えると、Ideone.comのC++ 4.8.1でコンパイルを通すことができました。

コード:

#define override

class ITest{
public:
 virtual void Updata() = 0;
 virtual ~ITest(){}
};
 
class CTest:public ITest{
public:
 virtual void Updata(){}override;
 void Draw();
};
 
int main(){
 ITest *ptr;
 ptr = new CTest();
 ptr->Updata();
 ((CTest*)ptr)->Draw(); /*エラーがでる。描画関数を呼び出したい*/
}

void CTest::Draw() {}
回答ありがとうございます!
なるほど、ダウンキャストする場合、ちゃんと括弧につけなければならないんですね!

Re: C++における、抽象クラスについて

Posted: 2014年5月06日(火) 14:46
by Bone
usao さんが書きました:
オフトピック
ダウンキャストしないといけない場面なのでしょうか
そういうことになりますね。

Re: C++における、抽象クラスについて

Posted: 2014年5月06日(火) 14:55
by みけCAT
Bone さんが書きました:なるほど、ダウンキャストする場合、ちゃんと括弧につけなければならないんですね!
ダウンキャストは関係ありません。
同様の原因でエラーになる例を示します。
コードはC言語ですが、C++でも同じだと思います。

コード:

#include <stdio.h>
#include <stdlib.h>

typedef struct {
	int a,b;
} hoge;

int comp(const void* x,const void* y) {
#if 0
	/* エラー */
	if((const hoge*)x->a > (const hoge*)y->a) return 1;
	if((const hoge*)x->a < (const hoge*)y->a) return -1;
#else
	/* OK */
	if(((const hoge*)x)->a > ((const hoge*)y)->a) return 1;
	if(((const hoge*)x)->a < ((const hoge*)y)->a) return -1;
#endif
	return 0;
}

int main(void) {
	hoge h[10];
	int i;
	for(i=0;i<10;i++) {
		h[i].a=(i*3)%10;
		h[i].b=i;
	}
	for(i=0;i<10;i++) {
		printf("%d %d\n",h[i].a,h[i].b);
	}
	puts("-----------");
	qsort(h,10,sizeof(hoge),comp);
	for(i=0;i<10;i++) {
		printf("%d %d\n",h[i].a,h[i].b);
	}
	return 0;
}

Re: C++における、抽象クラスについて

Posted: 2014年5月06日(火) 15:04
by Bone
softya(ソフト屋) さんが書きました:ダウンキャスト(親→子)は本来違う子をキャストしてしまう可能性があり危険なので出来るだけ避けるのが鉄則です。
なので、ダウンキャストが起こる設計自体を見なおしたほうが良いですね。

※ せっかくの抽象化を無意味にしてしまうので、オブジェクト指向的にも良くないと思います。
なるほど、やはりダウンキャストは使うべきでは、ないのですね。
いまいち、仮想クラスを用いて、ポリモーフィズムを使う場面というのがわからず、ゲームプログラミングの館においての、シーン変更などの実用例を見ても、あまりぴんと来ません。
少し、疑問におもっていることなのですが、

コード:

#include <iostream>
class ITest{
public:
 virtual void Updata() = 0;
 virtual ~ITest(){}
};
 
class CTest1:public ITest{

class ITest{
public:
 virtual void Updata() = 0;
 virtual ~ITest(){}
};
 
class CTest1:public ITest{
public:
 virtual void Updata()override{}
 void Draw(){};
};

class CTest2:public ITest{
public:
 virtual void Updata() override{
  //Draw();
 }
 void Draw();
};

int main(){
 ITest *ptr[2];
 ptr[0] = new CTest1();
 ptr[1] = new CTest2();
 for(int i = 0; i < 2; i++){
  ptr[i]->Updata();
 } 
}
このような使い方をした場合外部参照でエラーになります。
この場合においても、やはり、ダウンキャストしなければならないのでしょうか?

Re: C++における、抽象クラスについて

Posted: 2014年5月06日(火) 15:08
by Bone
みけCAT さんが書きました:
Bone さんが書きました:なるほど、ダウンキャストする場合、ちゃんと括弧につけなければならないんですね!
ダウンキャストは関係ありません。
同様の原因でエラーになる例を示します。
コードはC言語ですが、C++でも同じだと思います。

コード:

#include <stdio.h>
#include <stdlib.h>

typedef struct {
	int a,b;
} hoge;

int comp(const void* x,const void* y) {
#if 0
	/* エラー */
	if((const hoge*)x->a > (const hoge*)y->a) return 1;
	if((const hoge*)x->a < (const hoge*)y->a) return -1;
#else
	/* OK */
	if(((const hoge*)x)->a > ((const hoge*)y)->a) return 1;
	if(((const hoge*)x)->a < ((const hoge*)y)->a) return -1;
#endif
	return 0;
}

int main(void) {
	hoge h[10];
	int i;
	for(i=0;i<10;i++) {
		h[i].a=(i*3)%10;
		h[i].b=i;
	}
	for(i=0;i<10;i++) {
		printf("%d %d\n",h[i].a,h[i].b);
	}
	puts("-----------");
	qsort(h,10,sizeof(hoge),comp);
	for(i=0;i<10;i++) {
		printf("%d %d\n",h[i].a,h[i].b);
	}
	return 0;
}
つまり
((Player*)ptr)のような形にして、アローを飛ばさないと、ptrだけのアドレスが飛ばされているということでしょうか?

Re: C++における、抽象クラスについて

Posted: 2014年5月06日(火) 15:11
by softya(ソフト屋)
コピペをミスっていませんか? このコードですよね?

コード:

#include <iostream>
class ITest {
public:
	virtual void Updata() = 0;
	virtual ~ITest() {}
};

class CTest1: public ITest {
public:
	virtual void Updata()override {}
	void Draw() {};
};

class CTest2: public ITest {
public:
	virtual void Updata() override {
		//Draw();
	}
	void Draw();
};

int main() {
	ITest *ptr[2];
	ptr[0] = new CTest1();
	ptr[1] = new CTest2();
	for( int i = 0; i < 2; i++ ) {
		ptr[i]->Updata();
	}
}

Re: C++における、抽象クラスについて

Posted: 2014年5月06日(火) 15:17
by softya(ソフト屋)
とりあえず、これじゃダメなのでしょうか? ダメなら理由を教えて下さい。

コード:

#include <iostream>
class ITest {
public:
	virtual void Updata() = 0;
	virtual void Draw() = 0;
	virtual ~ITest() {}
};

class CTest1: public ITest {
public:
	virtual void Updata() { std::cout << "CTest1 Updata()" << std::endl;  }
	void Draw() { std::cout << "CTest1 Draw()" << std::endl; };
};

class CTest2: public ITest {
public:
	virtual void Updata() { std::cout << "CTest2 Updata()" << std::endl;  }
	void Draw() { std::cout << "CTest2 Draw()" << std::endl; };
};

int main() {
	ITest *ptr[2];
	ptr[0] = new CTest1();
	ptr[1] = new CTest2();
	for( int i = 0; i < 2; i++ ) {
		ptr[i]->Updata();
		ptr[i]->Draw();
	}
}

Re: C++における、抽象クラスについて

Posted: 2014年5月06日(火) 15:27
by Bone
softya(ソフト屋) さんが書きました:とりあえず、これじゃダメなのでしょうか? ダメなら理由を教えて下さい。

コード:

#include <iostream>
class ITest {
public:
	virtual void Updata() = 0;
	virtual void Draw() = 0;
	virtual ~ITest() {}
};

class CTest1: public ITest {
public:
	virtual void Updata() { std::cout << "CTest1 Updata()" << std::endl;  }
	void Draw() { std::cout << "CTest1 Draw()" << std::endl; };
};

class CTest2: public ITest {
public:
	virtual void Updata() { std::cout << "CTest2 Updata()" << std::endl;  }
	void Draw() { std::cout << "CTest2 Draw()" << std::endl; };
};

int main() {
	ITest *ptr[2];
	ptr[0] = new CTest1();
	ptr[1] = new CTest2();
	for( int i = 0; i < 2; i++ ) {
		ptr[i]->Updata();
		ptr[i]->Draw();
	}
}
すみません、先ほどのは//Draw()ではなく、Draw()です。
紛らわしいネーミングにしていますが、実際は、Updata()の中に、updataの処理(関数)を入れようと思っていました。
実行した結果、外部参照にあったため、コンソールで確認したときのコードを使いました。
やはり、別の方法を考えるべきでしょうか?

Re: C++における、抽象クラスについて

Posted: 2014年5月06日(火) 15:28
by Bone
だめであるわけでは有りません。
ただ、確認したく思いました。
言葉足らずですみません。

Re: C++における、抽象クラスについて

Posted: 2014年5月06日(火) 15:31
by softya(ソフト屋)
やはり、やりたいことが分かりません。
コードで書いてくださいね。

これでもないのでしょうか?

コード:

#include <iostream>
class ITest {
public:
	virtual void Updata() = 0;
	virtual void Draw() = 0;
	virtual ~ITest() {}
};

class CTest1: public ITest {
public:
	virtual void Updata() { std::cout << "CTest1 Updata()" << std::endl;  func(); }
	void Draw() { std::cout << "CTest1 Draw()" << std::endl; };
	void func() { std::cout << "CTest1 func()" << std::endl;  };
};

class CTest2: public ITest {
public:
	virtual void Updata() { std::cout << "CTest2 Updata()" << std::endl;  funcx(); }
	void Draw() { std::cout << "CTest2 Draw()" << std::endl; };
	void funcx() { std::cout << "CTest2 funcx()" << std::endl;  };
};

int main() {
	ITest *ptr[2];
	ptr[0] = new CTest1();
	ptr[1] = new CTest2();
	for( int i = 0; i < 2; i++ ) {
		ptr[i]->Updata();
		ptr[i]->Draw();
	}
}

Re: C++における、抽象クラスについて

Posted: 2014年5月06日(火) 15:34
by Tatu
本題には関係ありませんが
updataってupdate(アップデート,最新にする)じゃないんですか?

Re: C++における、抽象クラスについて

Posted: 2014年5月06日(火) 15:36
by softya(ソフト屋)
あっupdataじゃなくupdateっぽいですね。
すいません見過ごしてました。
※ こういうミスに気づきにくい softya(ソフト屋)です。

Re: C++における、抽象クラスについて

Posted: 2014年5月06日(火) 15:44
by Bone
softya(ソフト屋) さんが書きました:やはり、やりたいことが分かりません。
コードで書いてくださいね。

これでもないのでしょうか?

コード:

#include <iostream>
class ITest {
public:
	virtual void Updata() = 0;
	virtual void Draw() = 0;
	virtual ~ITest() {}
};

class CTest1: public ITest {
public:
	virtual void Updata() { std::cout << "CTest1 Updata()" << std::endl;  func(); }
	void Draw() { std::cout << "CTest1 Draw()" << std::endl; };
	void func() { std::cout << "CTest1 func()" << std::endl;  };
};

class CTest2: public ITest {
public:
	virtual void Updata() { std::cout << "CTest2 Updata()" << std::endl;  funcx(); }
	void Draw() { std::cout << "CTest2 Draw()" << std::endl; };
	void funcx() { std::cout << "CTest2 funcx()" << std::endl;  };
};

int main() {
	ITest *ptr[2];
	ptr[0] = new CTest1();
	ptr[1] = new CTest2();
	for( int i = 0; i < 2; i++ ) {
		ptr[i]->Updata();
		ptr[i]->Draw();
	}
}
やりたい言葉を簡潔に述べますと、敵と味方の共通点を多様性を用いて実行します。
が、敵クラス、味方クラスは共通メソッド以外にもそれぞれのメソッドを持っています。
敵クラスの更新と味方クラスの更新で、おのおののメソッドをおのおののupdate内で利用したいと思います。
そう思いまして、先ほどのコードを書きました。
コードを今拝見したのですが、overrideは必要なしなんでしょうか?
このあたりの理解がまだ深くないため、初歩的なことでもうしわけないです。

Re: C++における、抽象クラスについて

Posted: 2014年5月06日(火) 15:46
by Bone
Tatu さんが書きました:本題には関係ありませんが
updataってupdate(アップデート,最新にする)じゃないんですか?
いままで、updataだと、勘違いしてました・・・
updateに書き換えていきます・・・

Re: C++における、抽象クラスについて

Posted: 2014年5月06日(火) 15:51
by Bone
すみません、初歩的ミスをしていて、修正したところ、外部参照にはなりませんでした。
お騒がせしました。

Re: C++における、抽象クラスについて

Posted: 2014年5月06日(火) 15:57
by softya(ソフト屋)
すいません雑でした。 overrideは付けた方がミスが減らせると思います。

コード:

#include <iostream>
class ITest {
public:
	virtual void Update() = 0;
	virtual void Draw() = 0;
	virtual ~ITest() {}
};

class CTest1: public ITest {
public:
	virtual void Update() override { std::cout << "CTest1 Update()" << std::endl;  func(); }
	virtual void Draw() override { std::cout << "CTest1 Draw()" << std::endl; };
	void func() { std::cout << "CTest1 func()" << std::endl;  };
};

class CTest2: public ITest {
public:
	virtual void Update() override { std::cout << "CTest2 Update()" << std::endl;  funcx(); }
	virtual void Draw() override { std::cout << "CTest2 Draw()" << std::endl; };
	void funcx() { std::cout << "CTest2 funcx()" << std::endl;  };
};

int main() {
	ITest *ptr[2];
	ptr[0] = new CTest1();
	ptr[1] = new CTest2();
	for( int i = 0; i < 2; i++ ) {
		ptr[i]->Update();
		ptr[i]->Draw();
	}
}

Re: C++における、抽象クラスについて

Posted: 2014年5月06日(火) 16:05
by Bone
softya(ソフト屋) さんが書きました:すいません雑でした。 overrideは付けた方がミスが減らせると思います。

コード:

#include <iostream>
class ITest {
public:
	virtual void Update() = 0;
	virtual void Draw() = 0;
	virtual ~ITest() {}
};

class CTest1: public ITest {
public:
	virtual void Update() override { std::cout << "CTest1 Update()" << std::endl;  func(); }
	virtual void Draw() override { std::cout << "CTest1 Draw()" << std::endl; };
	void func() { std::cout << "CTest1 func()" << std::endl;  };
};

class CTest2: public ITest {
public:
	virtual void Update() override { std::cout << "CTest2 Update()" << std::endl;  funcx(); }
	virtual void Draw() override { std::cout << "CTest2 Draw()" << std::endl; };
	void funcx() { std::cout << "CTest2 funcx()" << std::endl;  };
};

int main() {
	ITest *ptr[2];
	ptr[0] = new CTest1();
	ptr[1] = new CTest2();
	for( int i = 0; i < 2; i++ ) {
		ptr[i]->Update();
		ptr[i]->Draw();
	}
}
コピペして、実行してみました!
これが出来るのであれば、何とかなりそうです!
回答してくださったかたがた、ありがとうございました!

Re: C++における、抽象クラスについて

Posted: 2014年5月06日(火) 17:55
by Mana
敵と味方の共通点ってことならこういうことではないのかな?

コード:

#include <iostream>
class ITest {
public:
    virtual void Update() = 0;
    virtual void Draw() = 0;
    virtual ~ITest() {}
};

class CPlayerBase : public ITest {
public:
	virtual void Func() { std::cout << "CPlayerBase Func()" << std::endl; }
};

class CEnemyBase : public ITest {
public:
	virtual void Func() { std::cout << "CEnemyBase Func()" << std::endl; }
};
 
class CPlayer1 : public CPlayerBase {
public:
    virtual void Update() override { std::cout << "CPlayer1 Update()" << std::endl;  Func(); }
    virtual void Draw() override { std::cout << "CPlayer1 Draw()" << std::endl; };
};

class CEnemy1: public CEnemyBase {
public:
    virtual void Update() override { std::cout << "CEnemy1 Update()" << std::endl;  Func(); }
    virtual void Draw() override { std::cout << "CEnemy1 Draw()" << std::endl; };
};

class CEnemy2: public CEnemyBase {
public:
    virtual void Update() override { std::cout << "CEnemy2 Update()" << std::endl;  Func(); }
    virtual void Draw() override { std::cout << "CEnemy2 Draw()" << std::endl; };
};
 
int main() {
    ITest *ptr[3];
    ptr[0] = new CPlayer1();
    ptr[1] = new CEnemy1();
    ptr[2] = new CEnemy2();
    for( int i = 0; i < 3; i++ ) {
        ptr[i]->Update();
        ptr[i]->Draw();
    }
    return 0;
}

Re: C++における、抽象クラスについて

Posted: 2014年5月06日(火) 22:40
by Bone
Mana さんが書きました:敵と味方の共通点ってことならこういうことではないのかな?

コード:

#include <iostream>
class ITest {
public:
    virtual void Update() = 0;
    virtual void Draw() = 0;
    virtual ~ITest() {}
};

class CPlayerBase : public ITest {
public:
	virtual void Func() { std::cout << "CPlayerBase Func()" << std::endl; }
};

class CEnemyBase : public ITest {
public:
	virtual void Func() { std::cout << "CEnemyBase Func()" << std::endl; }
};
 
class CPlayer1 : public CPlayerBase {
public:
    virtual void Update() override { std::cout << "CPlayer1 Update()" << std::endl;  Func(); }
    virtual void Draw() override { std::cout << "CPlayer1 Draw()" << std::endl; };
};

class CEnemy1: public CEnemyBase {
public:
    virtual void Update() override { std::cout << "CEnemy1 Update()" << std::endl;  Func(); }
    virtual void Draw() override { std::cout << "CEnemy1 Draw()" << std::endl; };
};

class CEnemy2: public CEnemyBase {
public:
    virtual void Update() override { std::cout << "CEnemy2 Update()" << std::endl;  Func(); }
    virtual void Draw() override { std::cout << "CEnemy2 Draw()" << std::endl; };
};
 
int main() {
    ITest *ptr[3];
    ptr[0] = new CPlayer1();
    ptr[1] = new CEnemy1();
    ptr[2] = new CEnemy2();
    for( int i = 0; i < 3; i++ ) {
        ptr[i]->Update();
        ptr[i]->Draw();
    }
    return 0;
}
回答拝見しました!
感覚的にはこんなかんじです!
ありがとうございます!