SSブログ

C++ クラス -インターフェースクラス- [開発環境]

参考サイト

http://www.geocities.jp/ky_webid/index_old.html

◆インターフェースクラス

名前の通り、クラスの一種ですが、少々特徴的なものです。 なお、単にインタフェースと呼ぶこともありますし、抽象クラスと呼ぶこともあります。 (インタフェースクラスという言葉は、C++以外で使われることが多く、どちらかというと C++ では使われる頻度の低い言葉ですが、 本サイトではこの言葉で統一します)。

インタフェースクラスは、メンバ変数を持たず、メンバ関数だけで構成されます。 そして、それらのメンバ関数は、宣言だけされていて定義がありません。 定義のないメンバ関数を持っている都合上、インタフェースクラスをインスタンス化することもできません。 一見、何のために存在しているか分からないですが、インタフェースクラスは、継承して使うためにあります。 そして、定義のないメンバ関数達は、オーバーライドして使います。

書式

// インタフェースクラスの定義
class IPen
{
public:
// 線を描く
virtual void DrawLine(int sx, int sy, int ex, int ey) = 0;
};

上のようにすると、IPen という名前のインタフェースクラスを実現できます。 繰り返しますが、C++には直接的にインタフェースクラスは用意されていません。 他の言語、例えば Java というオブジェクト指向言語なら「interface」というキーワードが用意されています。

これまでのクラス定義と違うのは、メンバ関数の宣言方法だけです。 最後に「=0」が付いており、これを付けると、そのメンバ関数の定義を省略できます。 定義が省略されるので、このクラスをインスタンス化することはできなくなります。 インスタンス化できてしまうと、定義が省略されたメンバ関数を呼び出そうとするかも知れないからです。

なお、先頭の「virtual」キーワードも必要です。 virtualの付いたメンバ関数を仮想関数というのでしたが、更に末尾に「=0」を付けると、純粋仮想関数と呼ばれます。

なお、クラス名の先頭に「C」ではなく「I」を付けました。 もちろんInterfaceの意味です。これは、通常のクラスとの区別に役立ちます。

このように、C++では、直接的にインタフェースクラスを記述できず、純粋仮想関数を宣言してやることで、 インスタンス化を妨害するような形で実現させています。

そのため、メンバ変数を追加したり、純粋仮想関数でないメンバ関数を加えたりすることもできてしまいます。 しかし、それらを加えた時点で、厳密にはインタフェースクラスとは呼べなくなります。 実際、Java の interface では、メンバ変数を宣言できなくなっており、メンバ関数も自動的に純粋仮想関数扱いになるようになっています (今は C++編なので関係ないですが、Java ではメンバ変数やメンバ関数という用語は使いません。また純粋仮想関数とも呼ばないです)。


コーディング例

先程定義した IPenインタフェースクラスを使ってみましょう。 前述の通り、インタフェースクラス(というより、純粋仮想関数を含んでいるクラス)はインスタンス化ができません。 そのため、必ず継承を行い、そのサブクラス側の方をインスタンス化することになります。


// pen.h

// CColorPenクラスで使う線の色
#define BLACK (0)
#define RED (1)
#define BLUE (2)
#define GREEN (3)

// インタフェースクラスの定義
class IPen
{
public:
// 線を描く
virtual void DrawLine(int sx, int sy, int ex, int ey) = 0;
};

// 黒いペンのクラス
class CPen : public IPen
{
public:
// 線を描く
void DrawLine(int sx, int sy, int ex, int ey);
};

// 色ペンのクラス
class CColorPen : public IPen
{
public:
// コンストラクタ
CColorPen();
// 線を描く
void DrawLine(int sx, int sy, int ex, int ey);
// 線の色を設定する
void SetColor(int color);

private:
// 線の色
int m_color;
};
 

まず、クラス定義は上のようになります。 インタフェースクラスといえど、C++ では普通のクラスと定義の方法自体は変わらないので、普通に継承できます。 そして、純粋仮想関数は、特別な形の仮想関数に過ぎないのでオーバーライドできます。オーバーライドした純粋仮想関数の定義の仕方についても、何も特別なことはありません。 普通のメンバ関数と同じです。ほとんど、オーバーライドと同じですが、メンバ関数の定義は次のようになります。


// pen.cpp
#include "pen.h"

// 黒い線を描く
void CPen::DrawLine(int sx, int sy, int ex, int ey)
{
// 黒い線を描く
}

// CColorPenクラスのコンストラクタ
CColorPen::CColorPen()
{
m_color = RED; // 赤いペンを初期カラーとする
}

// 現在設定されている色で線を描く
void CColorPen::DrawLine(int sx, int sy, int ex, int ey)
{
// m_colorを参照して、その色で線を描く
}

// 線の色を設定する
void CColorPen::SetColor(int color)
{
m_color = color;
}
 
最後にmain関数を見てみます。
 
// main.cpp
#include "pen.h"

int main()
{
CPen pen;
CColorPen color_pen;

pen.DrawLine( 10, 10, 100, 100 ); // 黒い線を描く
color_pen.SetColor( BLUE ); // 青色を設定
color_pen.DrawLine( 20, 20, 200, 200 ); // 青い線を描く

return 0;
}
 

やはりオーバーライドと変わりません(色は変えましたが)。 ここで、試しに次のようにしてみてください。

IPen ipen;

するとコンパイルが通らないことが分かります。 理由は IPenクラスには純粋仮想関数が含まれているからです。デストラクタを宣言するときに、virtualキーワードを付けると、仮想デストラクタになります。今の時点では理由が説明できないのですが、インタフェースクラスには仮想デストラクタを宣言しておくべきです。 ある状況下では、仮想デストラクタがないと処理が正しく行えません。 たとえ、デストラクタで行う処理がなくとも、仮想デストラクタだけは宣言しておきましょう。 そして、その定義は空にしておきます。

// インタフェースクラスの定義
class IPen
{
public:
// 仮想デストラクタ
virtual ~IPen(){}
// 線を描く
virtual void DrawLine(int sx, int sy, int ex, int ey) = 0;
}; 

nice!(0)  コメント(0)  トラックバック(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。