プロが教えるわが家の防犯対策術!

オブジェクト指向プログラミングを学んでいます。
基本概念の多態性がどうも理解できません。

メリット、目的はなんなのでしょうか?

上位クラスを継承し、
その継承されたクラスのメソッドを上書きするということと何が違うのでしょうか?

学習の参考にしている書籍の中には
”多態性を使ったオブジェクト指向プログラミングの典型例”として、
異種リスト(=上位型に下位型を代入し、一族のオブジェクトを一括管理する)を紹介しています。
多態性はこのような使用のみを目的としているのでしょうか?

A 回答 (4件)

 異種リストを多態性を用いて実現するのは、一つの例ですね。

#2の方の方針も基本的には同じ応用例だと思います。

 じつは、多態性には、もっと重要な応用例があります。そして、これは現在のWindowsMFCプログラミングで多用されています。
 Windowsはイベントドリブン型の実行方式であることは、ご存知でしょうか?ここに大きなポイントがあるのです。

 いまクラス(CView)があるとしましょう。

class CView{
public:
  virtual void OnDraw(CDC* pDC);
};

 いま、このCViewクラスには仮想関数OnDrawがあったとします。これは、描画が必要になったときに、Windowsプロシージャから呼び出される関数です。

さらにここで、CViewからクラスCMyViewを派生させましょう。

class CMyView : public CView{
public:
  virtual void OnDraw(CDC* pDC);
};

 ここで、OnDrawの実体部に具体的な描画操作を書いておくとします。

 さて、いままでの話は準備でした。
ここからが本題です。

いま、アプリケーションのビューを登録するのに、
void Add(CView* pView);
となる関数が与えられているとしましょう。
この関数によってCViewが登録されると、描画が必要な際に、
  pView->OnDraw(pDC);
を自動で呼び出すようになっています。

もしここで、
  Add(new CMyView());
と登録すれば、描画が必要な際には自動的に、
CMyView::OnDraw(CDC* pDC)を呼び出してくれるわけです。
 これは通常の関数によるオーバーライドでは実現できません。オーバーライドしていたのでは、CView::OnDraw(CDC* pDC);が呼び出されてしまいます。

 もしかしたら、Add(CView* pView)について疑問が残るかもしれません。
 Add(CMyView* pMyView);という関数を作って、pMyView->OnDrawを呼び出せば、確かに同じ動作をしてくれるでしょう。
 しかし、Add(CView* pView);はライブラリで規定されている関数なのです。つまり、C言語でのprintfのようなもの。Add(CMyView* pMyView);という関数をつくるということは、ライブラリを有効利用しないということなのです。


 そのような観点から言えば、先ほどの異種リストも同じことが言えます。抽象度が高い「動作」の部分をライブラリにしておきさえすれば、具体的なものを考えたときに、すばやく目的の動作を実現できる。
 この概念はWindowsプログラミングのようにプログラム規模がとても大きくなる場合は、どうしても利用しなくてはならないでしょう。

 確かに多態性はわかりにくい概念ではあります。
しかし、抽象度の高いプログラムがあらかじめ作成されていて、それを具体的な目的で利用するといった場合、有効な方法なのです。

この回答への補足

回答、ありがとうございます!

説明していただいて、
さらに納得の度合いが増しました^^
これから、実際にどんどん実装してみて理解度を増してゆきたいと思います。

ひとつ確認させていただきたいのですが、
>もしここで、
>  Add(new CMyView());
>と登録すれば、描画が必要な際には自動的に、
>CMyView::OnDraw(CDC* pDC)を呼び出してくれるわけです。
> これは通常の関数によるオーバーライドでは実現できません。
>オーバーライドしていたのでは、CView::OnDraw(CDC* pDC);が呼び出されてしまいます。
は、書籍の中に説明にあったのですが、
通常の関数のオーバーライドでは上位型への回帰となる
(つまり、仮想関数のオーバーライドでないと、上位型メソッドのふるまいとなる)、
という理由からでしょうか?

補足日時:2005/06/11 10:40
    • good
    • 0

サブルーチンと対比すると分かりやすいと思います。


サブルーチンを利用すると呼び出されるプログラムを共通化できますが、多態性を利用すると呼び出し元のプログラムを共通化できます。

strategy パターンなどは、典型的な例だと思います。
http://www.atmarkit.co.jp/fdotnet/designptn/desi …

上記の様な説明は「オブジェクト指向でなぜつくるのか」という本に載っていました。
http://bpstore.nikkeibp.co.jp/item/main/14822281 …

この回答への補足

回答ありがとうございます!

>サブルーチンを利用すると呼び出されるプログラムを共通化できますが、
>多態性を利用すると呼び出し元のプログラムを共通化できます。
の部分を見て、
そういえば…!と、ふとどこかでその図を目にしたのを思い出しました。
今になって納得できた感じです。
#2さんのおっしゃっていたことをさらに理解できました。

>strategy パターンなどは、典型的な例だと思います。
>http://www.atmarkit.co.jp/fdotnet/designptn/desi …
ざっと見ていました。
まだ、よく分らないようです^^;
実際にコードを打ち込んでみたほうがいいみたいですね。
試してみたいと思います。

補足日時:2005/06/11 11:12
    • good
    • 0

多態性は非常によく使います。


そうとは意識しないで使っている場合も結構あると思います。

例えば、「処理の大部分は共通なのに一部分だけ違う」場合、
実現するのには次の3つが考えられます。

A.全て違うメソッドにする。
B.メソッドの中で、タイプ別に分岐処理する。
C.処理の内容が違う部分をオブジェクトにして多態性を使う。


Cの方法を使うとコードを共通化でき、さらにすっきりとさせることができます。


具体的には、ソートなんていうのがあります。

ソートというのは、

1.レコードを取り出す
2.レコードを比較する。
3.その結果によってレコードを入れ替える。

というのが概略です。

このうち、2だけは様々な方法が存在します。
(レコードのどこを比較するか、大小をどう比較するか等)

そこで、比較のロジックだけをオブジェクトにしてソートルーチンに渡します。


ソート(レコード群 records, 比較オブジェクト comp){

 recordsからレコード1を取り出す
 recordsからレコード2を取り出す

 int r = comp.compare(レコード1,レコード2)

 rが正の数なら入れ替え

}


昇順でソートしたければ、そのような結果を返すように「比較オブジェクト」のcompareメソッドを
上書きしたクラスを定義しておくわけです。


このように、多態性はいろんなところで使われています。

この回答への補足

回答、ありがとうございます。

なるほど便利ですね!
おっしゃっていることが理解できたら、
なんか感動してしまいました^^

補足日時:2005/06/11 01:38
    • good
    • 0

デザインパターンについて学ばれると多能性の意味が理解できるかと思います。

この回答への補足

回答、ありがとうございます。

学習を進めてゆき、
オブジェクト指向の基本的なことが理解出来次第、
デザインパターンも学んでみたいと思います。

補足日時:2005/06/11 01:35
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!