#pragma twice

KAB-studio > プログラミング > #pragma twice > 365 Version 17.10 仮想関数

#pragma twice 365 Version 17.10 仮想関数

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

 Version 17.10
仮想関数

前回はオーバーライドについて説明しました
基本クラスのメンバ関数を、派生クラスで上書きできるって機能だったよ

そう、オーバーライドを使うことで基本クラスのメンバ関数を修正したり
拡張したりできるわけです
……でもさ、水希ちゃんが言うほど重要な機能って気がしないよーな
今はまだね
実はビックリするようなのがあったりする?
あるんです、実は。今回はそのための機能のひとつ、【仮想関数】につい
て説明します
かそうかんすう?
そう。仮想関数というのは、ちょっと特殊なメンバ関数です。まず、
仮想関数を使用しない場合の例から


// 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" );
}


// 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;   // ここにブレークポイントをセット。
}


うん、別に普通だね
で、【ここにブレークポイントをセット。】でブレークポイントをセット
して、実行してみて
ほい。で?
 cDerivedData 変数を変数ウィンドウで見てみて
ほい。……【{...}】しかないよ

- cDerivedData  {...}
     CData     {...}

うん、それを確認して欲しかったんです
??? あ、これが仮想関数っていうのに継ながる?
そういうこと。では、仮想関数というものを作ってみます
おおー
って言っても、実はほんのちょっと追加するだけだけど

// Data.h

// CData クラス。
class CData
{
public:
    // Output() メンバ関数を、仮想関数にします。
    virtual void Output();
};

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

なんかほとんど変わってない……
追加したのは、【virtual】っていう単語

     追加
       ↓
    virtual void Output();

このように、メンバ関数の前に virtual をつけると、そのメンバ関数は
【仮想関数】になります
メンバ関数ではあるんだよね
そう、メンバ関数のちょっと特殊なもの、それが仮想関数
普通の関数は仮想関数にならないの?
ならないんです。それは仮想関数の仕組みが解れば分かるかな
そうそう、結局仮想関数ってなんなの? どういうメリットがあるの?
メリットは次回説明するとして、まずは仮想関数がなんなのか、という点
を説明します。まず、さっきと同じようにブレークポイントで止めて
ほい
で、変数ウィンドウで cDerivedData 変数の中を見てみて
って、メンバ変数は追加してないんだから変わらないんじゃ……あれ? 
なんか変なのが追加されてる!

- cDerivedData  {...}
  - CData     {...}
    - __vfptr  0x0042c13c const  CDerivedData::`vftable'
        [0] 0x00401050 CDerivedData::Output(void)

実は、仮想関数がひとつでもあると、この __vfptr というメンバ変数が
自動的に作られるんです
メンバ変数が自動的に作られる???
このメンバ変数は【仮想関数ポインタ】っていう特殊なポインタです。
__vfptr は Virual Function PoinTeR の略です
仮想関数ポインタ、そのままね
このポインタは、【仮想関数テーブル】っていう配列の変数 vftable を
指しています
!? ちょ、ちょっと待った、分からなくなったんだけど
図にするとこんな感じ

┌───────┐
│ cDerivedData │
│ ┌────┐ │
│ │ cData  │ │
│ │        │ │
│ │ __vfptr→→→→→
│ └────┘ │    ↓
└───────┘    ↓
                      ↓
       ←←←←←←←←
      ↓
      ↓
    vftable
  ┌─────────────────┐
 0│CDerivedData::Output()のアドレス  │
  └─────────────────┘

まず、基本クラスは派生クラスの中に入っている、っていうのは説明した
よね
 Version 17.02 ( No.357 ) で教わったよ
 __vfptr メンバ変数は、仮想関数のあるクラスに作られます。この
メンバ変数は配列へのポインタで、外にある vftable っていう配列の
アドレスを格納しています
それが vftable 、仮想関数テーブルね。でもなんか、中に関数が入ってる
けど……
仮想関数テーブルは、普通の配列じゃないんです。実は、仮想関数の
ポインタを格納した配列なんです
仮想関数、ってことはメンバ関数のポインタ!?
そう、 virtual を指定したメンバ関数のポインタが、この配列の中に
入れられるんです
?????
……ちょっと難しいね、というわけで続く!

/*
    Preview Next Story!
*/
わけわかんねー!
ちゃんと次回説明するから!
……なんか嫌な予感するなー
な、なに?
すっごく難しい話になりそうな予感
というわけで次回
< Version 17.11 仮想関数テーブルって? >
につづく!
だいじょうぶだよ、そんなに複雑じゃないし、それに面白いよ?
プログラミングが面白いわけねー!!
ええっ!?
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。