#pragma twice

KAB-studio > プログラミング > #pragma twice > 374 Version 17.19 インターフェイスの使い方

#pragma twice 374 Version 17.19 インターフェイスの使い方

前のページへ 表紙・目次へ 次のページへ

 Version 17.19
インターフェイスの使い方

「前回は」
『よく分からなかった!』
「というわけでちゃんとまとめてみます。まず、ポリモーフィズムの一番良
く使われる場面は?」
『関数ポインタの代わり!』
「そう、自分で作った関数を呼び出してもらうためにアドレスを渡すのが
関数ポインタ、それをもう少し分かりやすくしたのが」
『ポリモーフィズムの機能ってわけね』
「この部分だけを抜き出してみようか。まずは関数ポインタの場合」

■関数ポインタの場合
◇準備
                   (関数Aのアドレスを渡す)
┌──使われる側────┐         ┌────使う側───┐ 
│ ・関数Aを呼び出す関数 ←←←←←←・関数A             │
└───────────┘         └──────────┘

◇使う時
                     (関数Aを呼び出す)
┌──使われる側────┐         ┌────使う側───┐ 
│ ・関数Aを呼び出す関数 →→→→→→・関数A             │
└───────────┘         └──────────┘

『つまり、使う側で関数を用意してアドレスを渡しておくと、それを呼び出
してくれるわけね』
「そういうこと。これをポリモーフィズムで実現すると、こうなります」

■ポリモーフィズムの場合
◇準備
               (派生クラスの変数のアドレスを渡す)
┌──使われる側────┐         ┌────使う側──────┐ 
│ ・クラスAの仮想関数を ←←←←←←・クラスAの派生クラス。   │
│   を呼び出す関数     │         │  仮想関数をオーバーライド│
└───────────┘         └─────────────┘

◇使う時
               (クラスAの仮想関数を呼び出す)
┌──使われる側────┐         ┌────使う側──────┐ 
│ ・クラスAの仮想関数を →→→→→→・クラスAの派生クラス。   │
│   を呼び出す関数     │         │  仮想関数をオーバーライド│
└───────────┘         └─────────────┘

『あれ、こう比べてみるとだいぶ構造が違うような……』
「ポリモーフィズムの場合、まずクラスAというものを〈使われる側〉で用
意しておく必要があります。使う側は、その派生クラスを作って、仮想関数
をオーバーライドします」

     ┌───── クラスA──────┐ 
     │・仮想関数                    │
     └───────────────┘
                     △
                     │
                     │
     ┌───────┴───────┐ 
     │・仮想関数(オーバーライド)    │
     └── クラスAの派生クラス───┘

「このオーバーライドした仮想関数が、関数ポインタの時の関数Aに当たり
ます」
『つまりこれが〈使われる側〉から呼び出されるわけね』
「もうひとつ重要な点が、使う側は〈クラスAの派生クラス〉の変数を作っ
て、そのアドレスを渡すっていう点」
『関数ポインタの時は関数のアドレス、ポリモーフィズムの時はクラスの
変数』
「実はこれが重要。関数の時には関数を渡せるだけ、つまり変数は渡せない
んです」
『そっか、関数はグローバル変数使わなきゃいけないけど、クラスの変数な
らメンバ変数で渡せる!』
「そういうこと。つまり、関数ポインタの機能をバージョンアップしたもの
がポリモーフィズム、っていうことが言えるかな」
『便利になったわけねー。複雑になったけど……』
「確かに……。さて、話を戻してインターフェイスについて」
『あーそういえば』
「上の例で言うと、前回の DoBubbleSortUseComapre() 関数が〈使われる側〉
の〈クラスAの仮想関数をを呼び出す関数〉に当たります」

// ソートを行う関数です。
void DoBubbleSortUseComapre
    ( int *p_piAry, int p_iSize, CComparetor *p_pcComparetor )
// CComparetor クラスの仮想関数 CompareTo() メンバ関数を呼び出します

『この第3引数に CComparetor クラスの派生クラス、つまり 
CIntComparetor クラスの、変数のアドレスを渡すと、ポリモーフィズムが
効いて、オーバーライドしてる CompareTo() メンバ関数が呼び出される、
っていう仕組みね』
「そういうこと。で、ここでもう一度さっきの図を振り返ってみます」

◇準備
               (派生クラスの変数のアドレスを渡す)
┌──使われる側────┐         ┌────使う側──────┐ 
│ ・クラスAの仮想関数を ←←←←←←・クラスAの派生クラス。   │
│   を呼び出す関数     │         │  仮想関数をオーバーライド│
└───────────┘         └─────────────┘

『左が DoBubbleSortUseComapre() 関数、右が CIntComparetor クラスと
CompareTo() メンバ関数だね』
「この矢印の部分、右から左に CIntComparetor クラスの変数のアドレスを
渡す箇所が DoBubbleSortUseComapre() 関数の第3引数になります」

// ソートを行う関数です。
void DoBubbleSortUseComapre
    ( int *p_piAry, int p_iSize, CComparetor *p_pcComparetor )
                                                  ↑
                    ここから「派生クラスの変数のアドレス」を渡します

「つまり、この第3引数が、〈呼び出してもらうメンバ関数を持つクラス〉
の変数を渡す〈受付〉になっている、というわけ」
『受付……』
「さっきの図で言うと、この受付部分が」

                        ↓ここ
┌──使われる側────┐         ┌────使う側──────┐ 
│ ・クラスAの仮想関数を ←←←←←←・クラスAの派生クラス。   │
│   を呼び出す関数     │         │  仮想関数をオーバーライド│
└───────────┘         └─────────────┘

「になります。つまり、この部分が〈使われる側〉と〈使う側〉の接続口に
なっているわけです」
『確かにここで継ながってるね』
「で、これをいきなりですがパソコンとプリンタに置き換えます」
『パソコンとプリンタ???』

┌──パソコン─────┐         ┌─プリンタ──────┐ 
│ ・印刷の命令←←←←←←←←←←←・印刷する機能        │
│   を行うアプリ       │         │                      │
└───────────┘         └───────────┘

『あ、なんか似てる』
「これって似てるでしょう。プリンタはパソコンにケーブルで継なぎます。
パソコンはプリンタに対して印刷の命令を出すことで印刷します。この時、
プリンタはどんなメーカーのどんな機種でも印刷できるでしょ?」
『うん、プリンタのアイコン押せば印刷できるもんね。あ、それって
ポリモーフィズムってこと?』
「そういうこと! つまり、あらゆるプリンタは〈プリンタの基本命令〉を
持っていて、パソコンに接続することでパソコンはその命令を送るだけで
どんなプリンタでも印刷できるわけです」
『なるほどねー、〈プリンタの基本命令〉が基本クラスの仮想関数で、それ
ぞれのプリンタの印刷機能が派生クラスでオーバーライド、ってわけね。確
かにポリモーフィズムしてる』
「で、このケーブル接続用コネクタを、専門用語で【インターフェイス】っ
て言います」

                        ↓インターフェイス
┌──パソコン─────┐         ┌─プリンタ──────┐ 
│ ・印刷の命令←←←←←←←←←←←・印刷する機能        │
│   を行うアプリ       │         │                      │
└───────────┘         └───────────┘

『おー、つまりこのインターフェイスの部分に当たるから、基本クラスの
ことをインターフェイス、って言ってるんだ』
「そういうこと。こんな感じに、使う側と使われる側の接続部分になるのが
インターフェイス、というわけです」
『なるほどねー』
「ただし!」
『ただし?』
「全部の基本クラスがインターフェイスっていうわけじゃないんです。
インターフェイスは、次のようなクラスのことを指します」

・基本クラスは持たない(持つとしてもインターフェイスに限る)
・メンバ変数を持たない
・メンバ関数は純粋仮想関数のみ

『つまり前回の CComparetor クラスはインターフェイスってわけね』

// 比較用クラスの基本クラス。
class CComparetor
{
public:
    // 比較用メンバ関数。純粋仮想関数です。
    virtual bool CompareTo( int p_iL, int p_iR ) = 0;
};

『でもなんでこういうルールがあるの?』
「なぜかっていうと、中身があっちゃいけないから」
『中身?』
「パソコンとプリンタの例で言うと、インターフェイスは〈プリンタの機能〉
を持っていちゃいけない、ってこと」
『あ、なるほど。インターフェイスは接続口でプリンタじゃないから、機能
があっちゃいけないんだ』
「この CComparetor クラスの場合も、このクラスは比較機能とかは全く
持っていません。ただ、 CompareTo() メンバ関数を呼び出すための接続口
としてのみ機能します」
『だからインターフェイス、っていうわけね』
「まぁもっとも、こういうクラスをインターフェイスって呼んでいる、って
いうだけで、普通のクラスなんかと大きな違いはないんだけどね」
『それじゃ意味ないじゃん』

/*
    Preview Next Story!
*/
『おー、図にしてなんとか分かってきた感じ』
「見て分かるっていうのは重要かもね」
『まぁテキストじゃ限界あるだろうけどねー』
「確かにね、アスキーアートまで行くと環境によっては見られないし」
『やっぱりメルマガじゃ限界あるんじゃない?』
「う」
『というわけで次回』
< Version 17.20 インターフェイスを戻り値にする >
「につづく!」
『さっきのプリンターとかも、あんな四角じゃいまいちだし〜』
「http://images.google.co.jp/images?q=%E3%83%97%E3%83%AA%E3%83%B3%E3%82%BFッ」
『うわ力業で来た!』
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。