#pragma twice

KAB-studio > プログラミング > #pragma twice > 375 Version 17.20 戻り値の型をインターフェイスにする

#pragma twice 375 Version 17.20 戻り値の型をインターフェイスにする

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

 Version 17.20
戻り値の型をインターフェイスにする

前回はインターフェイスの具体的な説明をしました
つまり、外の関数を呼び出すための接続点になる、そういうクラスが
インターフェイスってことね
そういうこと。で、これをもう少し応用してみます
応用?
そう、インターフェイスを引数ではなく戻り値で使用します
……戻り値? 戻り値の型がインターフェイス、ってこと?
そういうこと。まず実際の例で見てみようか

// Main.cpp
#include <Windows.h>
#include <stdio.h>

// 出力を行うクラスのインターフェイス。
class CPrinter
{
public:
    // 出力関数。純粋仮想関数です。
    virtual void Output( const char *p_pch ) = 0;
};

// デバッグ出力を行うクラス。
class CDebugPrinter : public CPrinter
{
public:
    // 出力関数。オーバーライドしています。
    void Output( const char *p_pch )
    {
        // デバッグ出力します。
        OutputDebugString( p_pch );
    }
};


// ダイアログ出力を行うクラス。
class CDlgPrinter : public CPrinter
{
public:
    // 出力関数。オーバーライドしています。
    void Output( const char *p_pch )
    {
        // 標準出力に出力します。
        MessageBox( NULL, p_pch, "デバッグ", MB_OK );
    }
};

// フラグ用定数値。
const int DEBUG_PRINTER = 0;
const int DLG_PRINTER = 1;

// 出力クラスを返す関数。
// 引数が DEBUG_PRINTER なら CDebugPrinter クラスのポインタを、
// DLG_PRINTER なら CDlgPrinter クラスのポインタを返します。
CPrinter *GetPrinterInstance( int p_iFlag )
{
    // 両クラスの変数を static 変数として作っておきます。
    static CDebugPrinter cDebugPrinter;
    static CDlgPrinter cDlgPrinter;

    // フラグによって出力を変更します。
    if( p_iFlag == DEBUG_PRINTER )
    {
        return &cDebugPrinter;
    }
    // else
    return &cDlgPrinter;
}

int WINAPI WinMain
    ( HINSTANCE p_hInstance
    , HINSTANCE p_hPrevInstance
    , LPSTR p_pchCmdLine
    , int p_iCmdShow
    )
{
    // 出力用に、 CPrinter クラスのポインタを受け取ります。
    CPrinter *pcPrinter;

    // まずデバッグ用を受け取ります。
    pcPrinter = GetPrinterInstance( DEBUG_PRINTER );
    // 出力します。
    pcPrinter->Output( "あいうえお\n" );

    // 次にダイアログ用を受け取ります。
    pcPrinter = GetPrinterInstance( DLG_PRINTER );
    // 出力します。
    pcPrinter->Output( "あいうえお\n" );

    return 0;
}

まず、 CPrinter クラスがインターフェイスで、その派生クラスとして
CDebugPrinter クラスと CDlgPrinter クラスがあります
図にするとこんな感じ?

                ┌───────────┐
                │CPrinter クラス       │
                │・Output()            │
                │ (純粋仮想関数)    │
                └───────────┘
                            △
                            │
            ┌───────┴───────┐
            │                              │
┌─────┴─────┐    ┌──────┴────┐
│CDebugPrinter クラス  │    │CDlgPrinter クラス    │
│・Output()            │    │・Output()            │
│ (オーバーライド)  │    │ (オーバーライド)  │
└───────────┘    └───────────┘

そうそう、そうなります。 CPrinter クラスに Output() メンバ関数とい
う純粋仮想関数があって、それを各派生クラスでオーバーライドしていま

 CDebugPrinter クラスでは OutputDebugString() 関数で出力して、 
CDlgPrinter クラスでは MessageBox() 関数で出力してるわけね。……
ダイアログで出力することなんてある?
デバッグモードじゃなくリリースモードの時とか
そか、リリースモードでデバッグ……って変だけど、そういうときに使う
わけね
そういうこと。で、このクラスの変数を作ってポインタを返すのが、 
GetPrinterInstance() 関数。ここは抜き出して説明します。まずこの関数
の引数に渡すふたつのフラグを用意します

// フラグ用定数値。
const int DEBUG_PRINTER = 0;
const int DLG_PRINTER = 1;

// 出力クラスを返す関数。
// 引数が DEBUG_PRINTER なら CDebugPrinter クラスのポインタを、
// DLG_PRINTER なら CDlgPrinter クラスのポインタを返します。
CPrinter *GetPrinterInstance( int p_iFlag )
                                   ↑
           この引数に DEBUG_PRINTER か DLG_PRINTER を渡します。

この関数は、戻り値が CPrinter クラスのポインタになっています
インターフェイスのポインタ、っていうことはここがポリモーフィズムす
るってことなんだ
そういうこと。この戻り値で返すのは、以下の static 変数のどちらかの
アドレスになります

    // 両クラスの変数を static 変数として作っておきます。
    static CDebugPrinter cDebugPrinter;
    static CDlgPrinter cDlgPrinter;

 Version 6.02 ( No.102 ) でやったのだね
そう。 static メンバ変数とか static メンバ関数とは全然別だから注意
してね
はーい。確か、関数がなくなっても残る変数なんだよね
そう、普通の変数は関数から抜けるとなくなるんだけど、 static 変数は
最初に関数が呼ばれたときに作られたら、そのままずっと残ります
それって、そのあとでポインタを返しているから?

    // フラグによって出力を変更します。
    if( p_iFlag == DEBUG_PRINTER )
    {
        return &cDebugPrinter;
    }
    // else
    return &cDlgPrinter;

そう。ここで、引数によってどちらかのポインタを返しているんだけど
普通の変数だとなくなっちゃうから、ポインタが使えなくなっちゃうもん

 Version 4.13 ( No.063 ) で説明したとおり、アドレスが指す先の変数
がなくなっちゃうとポインタとして意味がないから、 static 変数にして
なくならないようにしているわけです
なんか変な気もするけど……
それは次回説明するから。で、このように、引数に渡す値によって、返さ
れる変数が変わります
 DEBUG_PRINTER を渡すと CDebugPrinter クラスの変数のアドレスが返さ
れて、 DLG_PRINTER を渡すと CDlgPrinter クラスの変数のアドレスが返さ
れるわけね
このどちらのクラスも CPrinter クラスの派生クラスだから
アドレスを返すとポリモーフィズムするわけね
そのアドレスを受け取っているのと使っているのが以下のところ

    // 出力用に、 CPrinter クラスのポインタを受け取ります。
    CPrinter *pcPrinter;

    // まずデバッグ用を受け取ります。
    pcPrinter = GetPrinterInstance( DEBUG_PRINTER );
    // 出力します。
    pcPrinter->Output( "あいうえお\n" );

    // 次にダイアログ用を受け取ります。
    pcPrinter = GetPrinterInstance( DLG_PRINTER );
    // 出力します。
    pcPrinter->Output( "あいうえお\n" );

つまり、 pcPrinter には cDebugPrinter 変数のアドレスが入ってるとき
もあれば、 cDlgPrinter 変数のアドレスが入ってるときもある、ってわけ


 ┌ GetPrinterInstance() ──────────────────┐
 │ cDebugPrinter                              cDlgPrinter   │
 │       ↓ DEBUG_PRINTER だと     DLG_PRINTERだと↓        │
 │       ↓ こちらを返す              こちらを返す↓        │
 │       →→→→→→→→→→→↓←←←←←←←←←         │
 └────────────── ↓ ─────────────┘
                                ↓
                            pcPrinter

こーすると pcPrinter がポリモーフィズムするわけね
というわけで次回に続く!

/*
    Preview Next Story!
*/
インターフェイスを戻り値にすることもできるんだねー
この方法が分かるとかなり応用範囲が広がるよ
でも static 変数使うのって変じゃない?
そう、本当は new を使うんだけどね
あ、そういえば!
というわけで次回
< Version 17.21 new / delete とポリモーフィズム >
につづく!
しかしそれがポリモーフィズムの発展を阻害するのです!
な、なんですか熱くなって
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。