#pragma twice

KAB-studio > プログラミング > #pragma twice > 364 Version 17.09 継承とオーバーライド

#pragma twice 364 Version 17.09 継承とオーバーライド

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

 Version 17.09
継承とオーバーライド

前回は、継承関係にあるポインタのキャストについて説明しました
結構驚いたかもあれは……
これはあとで使うから憶えておいてもらうとして、今回からは、継承で最
も重要な機能【オーバーライド】について説明します
あれ? 前に一度教わったよーな
 Version 3.23 ( No.048 ) で一度説明したけど、あの時は、前回の説明
で出てきた CDialog とダイアログクラスとの関係が中心だから、今回のと
はちょっと違うかな
どんなふうに?
今回の方がより基本的で重要、って所かな
重要……
というわけで、また一からオーバーライドについて説明します。まず
最初に断っておくと、オーバーロードとは違うからね
 Version 11.19 ( No.219 ) や Version 16.13 ( No.340 ) でやったのだ
ね。つかついこの前しっかり教わったし。あれとは違うの?
名前は似てるけど全然別物。だから混乱しないようにね
オーバーロードとは別、オーバーロードとは別、と
ではオーバーライドは何かっていうと、簡単に言うと

・基本クラスのメンバ関数を、派生クラスで上書きする。

ことをいいます
どゆこと? 上書き?
つまり基本クラスに元々あるメンバ関数を、派生クラスで作り直すって
こと。ま、これは実際に試してみた方が早いかな。以下の例は、 
CData クラスの Output() メンバ関数を、 CDerivedData クラスで
オーバーライドしている例

// Data.h

// CData クラス。
class CData
{
public:
    void Output();
};

// CData クラスの派生クラス。
class CDerivedData : public CData
{
public:
    // 基本クラスの同名メンバ関数を、オーバーライドしました。
    void Output();
};


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

#include "Data.h"

// 基本クラスの Output() メンバ関数。
void CData::Output()
{
    OutputDebugString( "CData::Output()\n" );
}

// 派生クラスの Output() メンバ関数。
// オーバーライドしています。
void CDerivedData::Output()
{
    OutputDebugString( "CDerivedData::Output()\n" );
}

あれ? 結構あっさりしているような……
うん、オーバーライドには特別な文法はないんです。ただ単に基本クラス
と全く同じ名前・引数・戻り値のメンバ関数を作ればいいだけ

// CData クラス。
class CData
{
public:
    void Output();  // ←このメンバ関数と、
};

// CData クラスの派生クラス。
class CDerivedData : public CData
{
public:
    // 基本クラスの同名メンバ関数を、オーバーライドしました。
    void Output();  // ←このメンバ関数が同じ!
};

これだけでオーバーライドできるんだ
それ以外は普通のメンバ関数を作るのと同じ。特別な文法は必要ありませ

同じ名前・引数・戻り値のメンバ関数を作るだけ、と
さて、このようにオーバーライドすると、基本クラスのメンバ関数ではな
く派生クラスのメンバ関数が呼び出されるようになります。というわけで
使用例はこんな感じ

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

#include "Data.h"

int WINAPI WinMain
    ( HINSTANCE p_hInstance
    , HINSTANCE p_hPrevInstance
    , LPSTR p_pchCmdLine
    , int p_iCmdShow
    )
{
    // CData クラスを使用します。
    CData cData;
    cData.Output();
    // CData::Output()

    // CDerivedData クラスを使用します。
    CDerivedData cDerivedData;
    cDerivedData.Output();
    // CDerivedData::Output()

    return 0;
}

なるほど、 CData クラスの時は CData クラスのが呼ばれて、

    // CData クラスを使用します。
    CData cData;
    cData.Output();
    // CData::Output()

 CDerivedData クラスの時は CDerivedData クラスのが呼ばれるってわけ


    // CDerivedData クラスを使用します。
    CDerivedData cDerivedData;
    cDerivedData.Output();
    // CDerivedData::Output()

このように、オーバーライドをすることで基本クラスのメンバ関数ではな
く派生クラスのメンバ関数が呼ばれるようになるわけです
ってことはオーバーライドされちゃった方はなくなっちゃうの?
ううん、なくならないよ。オーバーライドした方からオーバーライドされ
た方を呼び出す場合には、以下の文法を使用します

基本クラス名::オーバーライドされたメンバ関数();

たとえば以下のように呼び出します

// 派生クラスの Output() メンバ関数。
// オーバーライドしています。
void CDerivedData::Output()
{
    OutputDebugString( "CDerivedData::Output()\n" );

    // 基本クラスの Output() メンバ関数を呼び出します。
    CData::Output();
}

この〈 CData::Output(); 〉の箇所がそう。こうすることで出力結果が
以下のように変わります

    // CDerivedData クラスを使用します。
    CDerivedData cDerivedData;
    cDerivedData.Output();
    // CDerivedData::Output()
    // CData::Output()

つまり、オーバーライドした CDerivedData クラスの 
Output() メンバ関数が先に呼び出されて、その中で オーバーライドされた
CData クラスの Output() メンバ関数が呼び出される、というわけです
ちょっと待った! 質問質問!
はい火美ちゃん
えっと、まず

    CData::Output();

これって Version 16.26 ( No.353 ) の static メンバ関数の呼び方と似
てるんだけど
似てるけど全然別物です。これは分かりにくいけど、 static メンバ関数
とは全く関係ないから
わかりにけー!! もひとつ質問!
はい火美ちゃん
これってこれまで何度も出てきた、たとえば Version 14.31 ( No.298 ) 
の……

BOOL CSearchingDlg::OnInitDialog() 
{
    CDialog::OnInitDialog();
// 略

なんかのも同じ?
そう同じ。この例は、 CSearchingDlg クラスで CDialog クラスの 
OnInitDialog() メンバ関数をオーバーライドしているんです
で、この中で基本クラスの、つまり CDialog クラスの 
OnInitDialog() メンバ関数を呼び出している、と
これはよく使うパターンなんです。たとえば〈基本クラスのメンバ関数が
行う【基本の機能】をちょっとだけ拡張したい〉場合は

// 派生クラスの Output() メンバ関数。
// オーバーライドしています。
void CDerivedData::Output()
{
    // 【基本の機能】の前にしたい処理を書きます。

    CData::Output();    // 【基本の機能】を行います。

    // 【基本の機能】の後にしたい処理を書きます。
}

こうすれば、元々のメンバ関数が持っていた【基本の機能】の前後に処理
を加える、ってできるでしょ
つまり、こうすればオーバーライド前のメンバ関数を完全に上書きしない
でちょっとだけ拡張できる、ってわけね
そういうこと。 MFC だとこういう利用法の方が多いかな

/*
    Preview Next Story!
*/
んー、でもオーバーライドってあんまりメリット感じないかも
そう、この段階だとまだ全然メリットがないんです
この段階?
実は、仮想関数っていうのにすると話が全然違ってくるんです
かそーかんすう?
というわけで次回
< Version 17.10 仮想関数 >
につづく!
ここからがこの章の面白いところだから
面白い……?
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。