何ヶ月か前にも似たようなスレを立てていますが、今回はもうちょっと踏み込んだ(?)ところの質問です
オブジェクト指向について開発を通して現在進行形でお勉強中ですが
・クラス化
・カプセル化
・継承
の3つについては概ね理解し、また開発で実践できるようにもなりました
ですが多態性(ポリモーフィズム)についてイマイチ理解できません
調べてみても
プログラミング言語の各要素(定数、変数、式、オブジェクト、関数、メソッドなど)についてそれらが複数の型に属すること
なんてややこしいこと書いていて良く分からないですし
先生に聞くと「関数の多重定義が許されることや」と答えが返ってきましたが、イマイチ上の定義と結びつきません
自分なりに解釈しようとした結果は下のような答えになりました
ウチの学校で配布されているライブラリでは
DirectXのD3DXVECTOR3構造体を、そのまま"ベクトル"として使いますが
同時に"座標"としても使うことがあります
このように一つの要素(この場合はD3DXVECTOR3構造体)を"複数の用途"で利用すること
これが僕が出した答えなのですが、これでもあんまり釈然としません(と言うか全く違う気すらする)
現状ではゲームプログラミングに特化しているともいえるほど知識と経験がそっちの方面に偏っている僕にも分かるように
分かりやすい解釈と具体例(実用例)を教えてもらえないでしょうか?
オブジェクト指向プログラミングについて その2
オブジェクト指向プログラミングについて その2
♪僕たちは まだ森の中 抜け出そう 陽のあたる場所へ
Re: オブジェクト指向プログラミングについて その2
例えば2Dでゲームを作る際に弾の座標をX,Yという変数で、
同時に弾の移動量をVelocityX, VelocityYという変数で表すとします。
そして、x,yという変数をメンバに持つVector2Dという構造体を作る場合、
というような感じで書くことができます。
メリットとしては、
(定義する)変数の数が減る。
同じ役割を持った変数同士を纏めることができる。
という用な感じでしょうか。
同時に弾の移動量をVelocityX, VelocityYという変数で表すとします。
そして、x,yという変数をメンバに持つVector2Dという構造体を作る場合、
int X, Y;
int VerocityX, VerocityY;
// と定義していたのを
struct Vector2D{
int x,y;
};
// 構造体を使えば
Vector2D Position;
Vector2D Verocity;
メリットとしては、
(定義する)変数の数が減る。
同じ役割を持った変数同士を纏めることができる。
という用な感じでしょうか。
- softya(ソフト屋)
- 副管理人
- 記事: 11677
- 登録日時: 13年前
- 住所: 東海地方
- 連絡を取る:
Re: オブジェクト指向プログラミングについて その2
ゲームでも結構必要だと思うんですけどね。
クラスの多態性(ポリモーフィズム)はオーバーライドとオーバーロードを使うので、この2つの理解度は如何でしょうか?
クラスの多態性(ポリモーフィズム)はオーバーライドとオーバーロードを使うので、この2つの理解度は如何でしょうか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。
Re: オブジェクト指向プログラミングについて その2
涼雅さんの言っていることは多態化とは少し違うのでは・・・むしろ、多態化はコードが増えることが多いのでは。
多態化とは、“複数の用途で利用できる”、ではなく、“複数の実装を同じ振る舞いで扱う”ことだと思います。
これは変数の数を減らしたり同じ役割を持った変数同士を纏めることが目的ではなく、様々な型の動作を同じ条件で違う動作をさせること、それによるコードの構造化、及び抽象化を行う技術だと思います。
例えば、Wikipediaの項から例を取ります。
一つ目の例は、「文字を数字に変換する」という振る舞いをする関数、そして2つ目は「日付を文字列に変換」します。
しかし、これらは「文字列に変換する」という目的において同質であると言えます。
様々な型、文字列に変換出来る型は同じように変換できることが望ましいですよね。
そうすると2つ目のコードに行き着きます。
じゃあこれは何の役に立つか?
例えばゲームにおいて、「曲がる弾」と「直線に動く弾」があります。これらを一つ目の方法で書くと、「曲がって動く関数」と「直線に動く関数」を用意することになります。
しかしそうではなく、「弾が動く」という同じ性質でみて、「moveBullet」という関数を作り、「クラス毎に違う動き」をさせればよいのです。
そうすると、「動く」という点について全ての「弾」を同じように扱うことができます。
では、試しに実際にコードにします。
そして、この二つのクラスは弾を動かすという性質であるInterfaceBulletとして見ると、同じように扱えます。
プログラムでは、それぞれをInterfaceBullet*型として扱えます(C++がオブジェクト指向をサポートしているため)。
なぜなら、InterfaceBulletを継承したクラスは必ず「moveBullet」という振る舞いが定義されているからです。ただし、内部でどのような処理をしているかは関係ありません。
このとき、moveBulletは多態である(複数の型に属している)と言える、ということでしょう。
私はこのように考えています。
※わかりやすくするため、コードにコメントを書き足しました。
というのは違うと思います。自分の見解を説明してみます(私も、まだまだ初心者なので他の方の返信と合わせて参考程度に)。このように一つの要素(この場合はD3DXVECTOR3構造体)を"複数の用途"で利用すること
多態化とは、“複数の用途で利用できる”、ではなく、“複数の実装を同じ振る舞いで扱う”ことだと思います。
これは変数の数を減らしたり同じ役割を持った変数同士を纏めることが目的ではなく、様々な型の動作を同じ条件で違う動作をさせること、それによるコードの構造化、及び抽象化を行う技術だと思います。
例えば、Wikipediaの項から例を取ります。
何かの値を文字列形式に変換する最も単純な場合を考える。
string = StringFromNumber(number)
string = StringFromDate(date)
これらの違いについて。string = number.StringValue
string = date.StringValue
一つ目の例は、「文字を数字に変換する」という振る舞いをする関数、そして2つ目は「日付を文字列に変換」します。
しかし、これらは「文字列に変換する」という目的において同質であると言えます。
様々な型、文字列に変換出来る型は同じように変換できることが望ましいですよね。
そうすると2つ目のコードに行き着きます。
じゃあこれは何の役に立つか?
例えばゲームにおいて、「曲がる弾」と「直線に動く弾」があります。これらを一つ目の方法で書くと、「曲がって動く関数」と「直線に動く関数」を用意することになります。
しかしそうではなく、「弾が動く」という同じ性質でみて、「moveBullet」という関数を作り、「クラス毎に違う動き」をさせればよいのです。
そうすると、「動く」という点について全ての「弾」を同じように扱うことができます。
では、試しに実際にコードにします。
class InterfaceBullet
{
public:
~InterfaceBullet(){}
void moveBullet() = 0; //< 「弾を動かす」という、全ての弾における共通の動作を抽象的に宣言
};
class CurveBullet
:public InterfaceBullet
{
public:
void moveBullet()
{
/// 曲がりながら進む処理
}
};
class StraightLineBullet
:public InterfaceBullet
{
public:
void moveBullet()
{
/// 直線に進む処理
}
};
そして、この二つのクラスは弾を動かすという性質であるInterfaceBulletとして見ると、同じように扱えます。
プログラムでは、それぞれをInterfaceBullet*型として扱えます(C++がオブジェクト指向をサポートしているため)。
なぜなら、InterfaceBulletを継承したクラスは必ず「moveBullet」という振る舞いが定義されているからです。ただし、内部でどのような処理をしているかは関係ありません。
void test()
{
StraightLineBullet bulletLine;
CurveBullet bulletCurve;
std::vector<InterfaceBullet*> listBullet;
listBullet.push_back( &bulletLine ); //< 暗黙的にStraightLineBullet*型をInterfaceBullet*型に変換することが許可されている
listBullet.push_back( &bulletCurve ); //< 上と同じように暗黙的変換
for( auto it = listBullet.begin(); it != listBullet.end(); ++it )
{
it->moveBullet(); //< InterfaceBullet型のメソッドは、派生クラスで必ず定義されているので、呼び出せる。
}
};
私はこのように考えています。
※わかりやすくするため、コードにコメントを書き足しました。
最後に編集したユーザー nullptr on 2012年9月27日(木) 16:53 [ 編集 2 回目 ]
✜ で C ご ✜
: す + 注 :
¦ か + 文 ¦
: ? Is the は :
✜ order C++? ✜
: す + 注 :
¦ か + 文 ¦
: ? Is the は :
✜ order C++? ✜
糸冬
――――――――
制作・著作 NHK
――――――――
制作・著作 NHK
Re: オブジェクト指向プログラミングについて その2
学問的に正しくないことや定義の曖昧さを恐れず書くなら,多態性とはC++において
主に前者で使われる言葉ではありますが。
Polymorphism (computer science) - Wikipedia, the free encyclopediaの類型で言うなら,前者は「Subtype polymorphism (or inclusion polymorphism)」で,後者は「Parametric polymorphism」です。
# 関数の多重定義は後者や,「Ad-hoc polymorphism」で使われます。
ちなみに,座標は原点からのベクトルとして表現できます (位置ベクトル)。
なので,座標に使っているというよりも位置ベクトルとして使っていると考えれば,どちらもベクトルとして使っていることになります。
- 継承と仮想関数の利用,および基底クラスへのポインタ型/参照型へ派生クラスへのポインタ/参照を代入することで,同一の実行コードがデータによって異なる振る舞いをすること (実行時多態)
- templateを使って,同一のソースコードが型によって異なる振る舞いをすること (コンパイル時多態)
主に前者で使われる言葉ではありますが。
Polymorphism (computer science) - Wikipedia, the free encyclopediaの類型で言うなら,前者は「Subtype polymorphism (or inclusion polymorphism)」で,後者は「Parametric polymorphism」です。
# 関数の多重定義は後者や,「Ad-hoc polymorphism」で使われます。
ちなみに,座標は原点からのベクトルとして表現できます (位置ベクトル)。
なので,座標に使っているというよりも位置ベクトルとして使っていると考えれば,どちらもベクトルとして使っていることになります。
Re: オブジェクト指向プログラミングについて その2
>涼雅さん
う~ん、その考え方だと僕の解釈とほぼ同じになりますね…
でも他の方々のお答を見る限り僕達の解釈は間違っている…みたいですね
とにかく解答、ありがとうございました
>softyaさん
オーバーライドは基底クラスで定義された関数を派生クラスで「書きかえること」
…だったと思います
オーバーロードに関してはバッチリですが
オーバーライドに関してはちょっと自信がないですね…
>新月の獅子さん
なるほど…長文で例を示してくださってありがとうございます
Yuoさんの答えと合わせることでようやく理解できた、ように思います(苦笑)
性質は似ているけど内部の処理が違う関数
それらをクラス毎に用意してやることで管理を容易にするのですね
「弾を動かす」のは共通の目的なので同じ名前の関数にするけども
「どのように動かすか」をクラス毎に変えてやると
分かりやすい例えでした
ありがとうございます
>YuOさん
同一の実行コードがデータによって異なる振る舞いをすること
ですか、なるほど
これはつまり上で新月の獅子さんが示したように
同じ名前の関数でもクラスによって動作が異なること、が当てはまる訳ですね!
う~ん、その考え方だと僕の解釈とほぼ同じになりますね…
でも他の方々のお答を見る限り僕達の解釈は間違っている…みたいですね
とにかく解答、ありがとうございました
>softyaさん
え~と、オーバーロードは「多重定義」クラスの多態性(ポリモーフィズム)はオーバーライドとオーバーロードを使うので、この2つの理解度は如何でしょうか?
オーバーライドは基底クラスで定義された関数を派生クラスで「書きかえること」
…だったと思います
オーバーロードに関してはバッチリですが
オーバーライドに関してはちょっと自信がないですね…
>新月の獅子さん
なるほど…長文で例を示してくださってありがとうございます
Yuoさんの答えと合わせることでようやく理解できた、ように思います(苦笑)
性質は似ているけど内部の処理が違う関数
それらをクラス毎に用意してやることで管理を容易にするのですね
「弾を動かす」のは共通の目的なので同じ名前の関数にするけども
「どのように動かすか」をクラス毎に変えてやると
分かりやすい例えでした
ありがとうございます
>YuOさん
同一の実行コードがデータによって異なる振る舞いをすること
ですか、なるほど
これはつまり上で新月の獅子さんが示したように
同じ名前の関数でもクラスによって動作が異なること、が当てはまる訳ですね!
♪僕たちは まだ森の中 抜け出そう 陽のあたる場所へ