第15章 アップキャスト

 今回は継承を使ったクラスを関数に渡してみたいと思います。「何だ、それだけか」と思われるかもしれませんが、それだけじゃないんです、これが。


 では、今回の要点です。


 では、いってみましょう。


 今回は、ファイルを開く部分を別の関数(メンバ関数ではありません)にしたいと思います。ファイル名を入力し、そのファイルを開くという関数です。

 この際渡す引数は、やはり CFile もしくは CTextFile のオブジェクトです。ということで、早速やってみたいと思います。

#include <iostream.h>
#include "File.h"
#include "TextFile.h"

bool Open(CFile& rfile, const char* pszFlags)
{
    char buffer[512];

    cout << "ファイル名を指定して下さい > " >> flush;
    cin >> buffer;
    return rfile.Open(buffer, pszFlags);
}

bool Open(CTextFile& rfile, const char* pszFlags)
{
    char buffer[512];

    cout << "ファイル名を指定して下さい > " >> flush;
    cin >> buffer;
    return rfile.Open(buffer, pszFlags);
}

 型が違うからこうしたのですが...何というか、不経済ですね。

 そういえば第11章で、「派生クラスは基底クラスのメンバを全て持っている」と言いました。なら、派生クラスのオブジェクトを基底クラスへの参照に渡しても、動作に問題はなさそうです。基底クラスのメンバは全て持っているわけだから、データが多いことはあっても、足りないことはないはずです。

 そして、Open 関数は CFile のものを使っているので、その呼び出しに問題はないはずです。

 というわけで、CTextFile への参照をとる関数は消して、CFile への参照をとる関数だけを残しましょう。

プログラム
// TestFile.cpp
#include <iostream.h>
#include "MainDefs.h"
#include "File.h"
#include "TextFile.h"

bool Open(CFile& rfile, const char* pszFlags)
{
    char buffer[512];

    cout << "ファイル名を指定して下さい > " << flush;
    cin >> buffer;
    return rfile.Open(buffer, pszFlags);
}

void Write(CTextFile& rtxt)
{
    char buffer[512];

    cout << "何を書き込みますか > " << flush;
    cin >> buffer;
    rtxt.WriteString(buffer);
}

void Load(CFile& rbin)
{
    char buffer[512];
    int  nRead;

    nRead = rbin.Read(buffer, numof(buffer));
    buffer[nRead] = 0;
    cout << buffer << endl;
}

int main()
{
    CFile     bin;
    CTextFile txt;

    if(Open(txt, "w") == false)
        return 0;
    Write(txt);
    txt.Close();

    if(Open(bin, "r") == false)
        return 0;
    Load(bin);
    bin.Close();

    return 0;
}
実行結果例
ファイル名を指定して下さい > Test.txt
CFile::Open
CTextFile::ModifyFlags
何を書き込みますか > ペペロンチーノ
ファイル名を指定して下さい > Test.txt
CFile::Open
CFile::ModifyFlags
ペペロンチーノ

 おお、きちんと動きました。CTextFile の ModifyFlags が呼ばれています。

 このように、派生クラスのオブジェクトを基底クラスへの参照、ポインタに対して渡すことは可能です。このことをアップキャストと言います。


 しかし、ちょっと待って下さい。そういえば仮想関数を使っていましたね。これって何か影響あるんでしょうか?

 では、virtual を消してもう一度やってみましょう。

実行結果例
ファイル名を指定して下さい > Test.txt
CFile::Open
CFile::ModifyFlags
何を書き込みますか > ペスカトーレ
ファイル名を指定して下さい > Test.txt
CFile::Open
CFile::ModifyFlags
ペスカトーレ

 今度は CFile の ModifyFlags が呼ばれてしまいました。

 これは前回と同じ状況ですね。CFile の参照から呼ばれたから CFile の ModifyFlags が呼ばれたのです。そして、それを回避し、オブジェクトの本当の型に対する ModifyFlags を呼ぶために仮想関数を使うわけです。


 最後にもう一度仮想関数についてまとめます。

 普通は、呼び出すときに使った型に対応する関数が呼ばれます。しかし、仮想関数にしていれば、そのオブジェクトの本当の型に対応する関数が呼ばれるのです。


 では、今回の要点です。


 それでは、また次回まで。


第14章 仮想関数 | 第16章 派生と構築

Last update was done on 2000.8.21

この講座の著作権はロベールが保有しています