ページ 11

C++のオーバライド

Posted: 2011年7月06日(水) 08:28
by ひよこ
おはようございます。

C++での質問です。
オーバーライドを使いたいのですが
予想通りに動きません。これはどうしてなのでしょうか?

予想
最終的に
printf("%d\n",saList[1].Get());
の行動が
printf("%d\n",SaList[1]->Get());
と同じにしたいのですが。
2回目のsaList.Getでsbと表示したい。

コード:

class sa{
public:
	int a;
	virtual int Get(){
		printf("sa\n");
		return a;
	}
	sa(){a=0;}
};

class sb:public sa{
	int b;
public:
	int Get(){
		printf("sb\n");
		return b;
	}
	sb(){a=10,b=20;}
};

sa *SaList[2] = {
	//{new sa},
	//{new sb},
};

//クラスをまとめるリスト
sa *saList;

sa Make_StateList2(sa List[]){
	saList = List;
	return List[0];
}

int main(){
	//クラスのを作成(アドレスとびとび)
	SaList[0] = new sa();
	SaList[1] = new sb();
	
	//そのままの状態を表示
	printf("%d\n",SaList[0]->Get());
	printf("%d\n",SaList[1]->Get());

	//クラスリストに代入(配列にいれて添字でアクセスできるように)
	saList = new sa[2];
	saList[0] = *SaList[0];
	saList[1] = *SaList[1];

	//クラスリストに代入されたものを使う
	printf("%d\n",saList[0].Get());
	printf("%d\n",saList[1].Get());
環境はVC2008Expressです。
よろしくお願いします。

Re: C++のオーバライド

Posted: 2011年7月06日(水) 10:49
by バグ
こんな感じでしょうか?意図に反していたらまたごめんなさい。
それと、newで確保した領域はdeleteで解放するようにしたほうがいいですよ。

コード:

#include <cstdio>

class sa
{
public:
	int a;

	virtual int Get()
	{
		printf("sa\n");
		return a;
	}

	sa()
	{
		a = 0;
	}
};

class sb : public sa
{
private:
	int b;

public:
	int Get()
	{
		printf("sb\n");
		return b;
	}

	sb()
	{
		a=10;
		b=20;
	}
};

int main()
{
	// クラスのを作成(アドレスとびとび)
	sa* SaList[2];
	SaList[0] = new sa();
	SaList[1] = new sb();

	// そのままの状態を表示
	printf("%d\n",SaList[0]->Get());
	printf("%d\n",SaList[1]->Get());

	// クラスリストに代入(配列にいれて添字でアクセスできるように)
	sa** saList;
	saList = new sa*[2];
	saList[0] = SaList[0];
	saList[1] = SaList[1];

	// クラスリストに代入されたものを使う
	printf("%d\n",saList[0]->Get());
	printf("%d\n",saList[1]->Get());

	// 後始末
	delete SaList[0];
	delete SaList[1];
	delete[] saList;

	return 0;
}

Re: C++のオーバライド

Posted: 2011年7月06日(水) 11:04
by softya(ソフト屋)
codeタグの使い方が間違っていたので修正しました。投稿前にプレビューして確認して下さいね。

Re: C++のオーバライド

Posted: 2011年7月06日(水) 11:21
by maru
意図していることがよくわからないのですが、元のプログラムの
saList[1] = *SaList[1];
では sa のオブジェクトに対して sb のオブジェクトを代入してしまっています。
この場合の動作は...
言語仕様に詳しくないので、どうなるのが正しいのかよくわかりません。

もし、意図した動作が、
「派生されたクラスオブジェクトを派生元のクラスオブジェクト配列にいれて、派生元クラスとして動作させたい」
ということであれば、そのような動作をするコードは思いつきません。

あと、sa では a を public にする必要性はないように思われます。
private か protected でいいのでは?

コード:

class sa
{
public:
	sa(int a_=0) : a(a_) {}
	virtual int Get() const
	{
		printf("sa\n");
		return a;
	}
private: // or protected:
	int a;
};

class sb : public sa
{
public:
	sb() : sa(10), b(20) {}
	virtual int Get() const
	{
		printf("sb\n");
		return b;
	}
private:
	int b;
};
としたほうが良いでしょう。

【追記】
コンストラクタで設定した初期値が間違っていましたので、修正しました。

Re: C++のオーバライド

Posted: 2011年7月06日(水) 11:36
by softya(ソフト屋)
これはあまりやった事はないですが、
saList[1] = *SaList[1];
ではsbクラスの派生情報は消滅するはずです。

Re: C++のオーバライド

Posted: 2011年7月06日(水) 11:38
by GRAM
ひよこ さんが書きました: 予想
最終的に
printf("%d\n",saList[1].Get());
の行動が
printf("%d\n",SaList[1]->Get());
と同じにしたいのですが。
2回目のsaList.Getでsbと表示したい。

コード:

sa *saList;
//まずsaListはsaの並びであり、決してsbが並んでいるわけではない
int main(){
	//クラスのを作成(アドレスとびとび)
	SaList[0] = new sa();
	SaList[1] = new sb();
	
	//そのままの状態を表示
	printf("%d\n",SaList[0]->Get());
	printf("%d\n",SaList[1]->Get());

	//クラスリストに代入(配列にいれて添字でアクセスできるように)
	saList = new sa[2];
	saList[0] = *SaList[0];//ここでSaListの中身が何であれsaにスライスされる
	saList[1] = *SaList[1];//
saListの中身はあくまでsaです。どんなにがんばったとしても。
そのsaにsbを代入した場合、問答無用でsbのsaの部分だけが代入されスライスされます。

結局仮想関数が呼び出されることは未来永劫ありません。
基本的に仮想関数を呼び出すには基底クラスのポインタか参照にアップキャストするしかないということですね。
SaListのほうを使えばいいわけです。

Re: C++のオーバライド

Posted: 2011年7月06日(水) 11:47
by ひよこ
皆さん、ありがとうございます。
バグさん動きました。予想通りに動きました。
softyaさん、ありがとうございます。プレビュー確認して半角でないのが原因だと思いますのでこれからは気をつけます。
maruさん,softyaさん,GRAMさん
結局、派生前 =派生後をして派生情報は消滅したのですか。
なるほど、勉強になりました。

皆さんありがとうございました。