Index

HOME > プログラムTOP > C++



C++の壺・クラス篇1

 C++の基本テクニックのTipsです。
 クラス篇1では、クラスの基本的な機能とクラスの継承(派生)及び使用方法を扱います。
ページ項 目内 容
クラス篇1 クラスの基礎 クラスの宣言 / アクセス修飾子 / コンストラクタ / デストラクタ / メンバ / 静的メンバ / クラスの実装 / thisポインタ
クラスの継承 継承(派生) / メンバ / コンストラクタ / デストラクタ / オーバーライド / 仮想関数 / 抽象クラス
動的インスタンス クラスの実体の動的な作成と破棄
クラス篇2 friendキーワードfriendクラス / friend関数
constキーワード constオブジェクト / constメンバ関数 / constメンバ定数
演算子オーバーロード 演算子のオーバーロード / オーバーロード可能な演算子
クラス篇3 クラスの多重継承 複数のクラスを継承するクラスです
メンバ関数のinline化 処理速度を速める関数です
クラス・テンプレート 様々なデータ型を扱えるクラスです

sasaraan programming

Exposition

●クラスの基礎

/*** xarray.h ヘッダファイル ***/  @

/*** XArray クラスの宣言    ***/

class XArray 
{
private:     A
    int *item;    // 先頭要素へのポインタ D

protected: A
   static int size;  // サイズ  DE

public:       A
    // コンストラクタとデストラクタ
    XArray();                       B
    XArray(const XArray *a); B
    ~XArray();                      C

    // メンバ関数
    static int GetSize();                DE
    int GetItem(int index);                D
    void SetItem(int index, int value); D
};  
/*** xarray.cpp ソースファイル ***/
#include <memory.h>     // memcpy
#include "xarray.h"  @  // XArrayクラス

/*** XArray クラスの実装 ***/  F

// 静的メンバ変数
int XArray::size = 100; E

// 静的メンバ:サイズの取得  DE
int XArray::GetSize() 
{
    return size;
}

// コンストラクタ    B
XArray::XArray() 
{
    item = new int[size];
}

// コピー・コンストラクタ  B
XArray::XArray(const XArray *a) 
{
  Gthis->item = new int[size];
  Gmemcpy(this->item, 
                  a->item, sizeof(int) * size);
}

// デストラクタ    C
XArray::~XArray() 
{
    delete [] item;
}

// メンバ関数:値の取得  D
int XArray::GetItem(int index) 
{
    if (index >= 0 && index < size) {
        return *(item + index);
    } else {
        return -1;
    }
}

// メンバ関数:値の設定  D
void XArray::SetItem(int index, int value)
{
    if (index >= 0 && index < size) {
        *(item + index) = value;
    }
}    
/*** main.cpp ソースファイル ***/
#include <stdio>           // printf
#include <conio.h>        // getch
#include "xarray.h"  @  // XArrayクラス

// main 関数
void main() 
{
    int i;
    int n = XArray::GetSize(); //サイズ E

    XArray a;       B
    for (i=0; i<n; i++) {
        a.SetItem(i, i*2);  // 値の設定  D
    }

    XArray x(&a);  // コピーコンストラクタ B
    for (i=0; i<n; i++) {
        printf("%d,", x.GetItem(i)); //出力 D
    }

    if (getch()) return 0;
} 
【@クラスの宣言】
 通例、クラスの宣言はヘッダファイルに記述し、実装(関数の中身など)はソースファイルで行います。宣言だけでは実体はつくられません。使用する時は、
 #include "xarray.h"
のように、ヘッダファイルをインクルードします。

【Aアクセス修飾子】
 クラスでは、データの保護や公開をアクセス修飾子で制御することができます。
 クラス内部派生クラスクラス外部
private××
protected×
public
 private は省略可能ですが、いずれのアクセス修飾子も、定義の末端か次のアクセス修飾子までが有効範囲です。また、同じアクセス修飾子を繰返し記述することも可能です。

【Bコンストラクタ】
 クラスは、インスタンス(実体)が作成されると、まずコンストラクタが呼ばれます。コンストラクタの定義は必須ではありませんが、定義する時は次のルールに則って記述し、変数などの初期化を行います。

・ コンストラクタ名はクラス名と同じにする
・ コンストラクタはまったく値を返さないこと

 コンストラクタは引数を取ることができますが、引数のない既定のコンストラクタは必要です。
 サンプルでは、引数の異なる2つのコンストラクタが定義 ( = オーバーロード ) されています。このうち2つ目は、特にコピー・コンストラクタと呼ばれ、自クラスのインスタンス(実体)を参照してコピーする機能を持ちます。
 また、コンストラクタにおけるメンバの初期化は、以下のように明示的に行うことができます。

XArray::XArray():item(NULL) { ... }

 なお、配列にした時に呼び出すことができるのは、既定のコンストラクタだけです。

【Cデストラクタ】
 クラスでは、実体が破棄される時は、自動的にデストラクタが呼ばれます。デストラクタの定義は必須ではありませんが、定義する時は次のルールに則ってデストラクタを記述し、メモリの解放などの終了処理を行います。

・ デストラクタ名はクラス名の頭に ~ を付ける
・ デストラクタはまったく値を返さないこと

* コンストラクタとデストラクタに データ型や return文の記述をすることはできません。

【Dメンバ】
 クラスで独自に定義された要素を メンバ と呼びます。メンバには、データメンバ(変数・定数)とメンバ関数とがあります。
 メンバは、通常は " インスタンス名 . メンバ " 、ポインタの場合は " インスタンス名 -> メンバ " のようにしてアクセスします。

* サンプルのように、メンバ変数は private や protected で保護し、外部関数からのアクセスの際には、メンバ関数を経由して、内部でチェックしてから使用すると安全に使用できます。

【E静的メンバ】
 static をつけたメンバは、特に静的メンバと呼ばれ、すべてのインスタンス(実体)で共通に適用する値、または関数となります。
 静的メンバは、実装時に値を初期化しておくことが可能です。また、クラスのインスタンスがなくても、"クラス名 :: 静的メンバ" のように、インスタンス名を用いずにアクセスすることができます。ただし、静的メンバ関数内では、静的メンバ以外のメンバを使用することはできません。

【Fクラスの実装】
 関数の中身は、一行程度であれば宣言時に記述することがあります(次節のサンプルコードの宣言部を参照)が、通常はソースファイルに記述します。
 別ファイルに実装定義を記述する際は、特定のクラスのメンバ関数であることを示すために、関数名の前に "クラス名 :: " の記述が必要です。

【Gthisポインタ】
 サンプルコードで、メンバの前にある "this->" は thisポインタです。インスタンス(実体)が作成された時の自分を指す機能を持ちます。thisポインタは省略可能ですが、コピー・コンストラクタの中の、 "a->item, this->item" のように、他のインスタンスと区別する際に使用します。

●クラスの継承

/*** ヘッダファイル : test.h ***/
#include <stdio.h>     // printf 

// 基底クラス Test の宣言
class Test
{
protected:
    char text[20];  A
public:
    Test(){ printf("Test Start\n"); }; B
    ~Test(){ printf("Test End\n");};   C
    void Show();  A
    void Set();    D
};

// 派生クラス XTest の宣言
class XTest : public Test  @
{
public:
    XTest(){ printf("XTest Start\n");}; B
    ~XTest(){ printf("XTest End\n");};  C
    void Set();    D
};

/*** ソースファイル : test.cpp ***/
#include <stdio.h>     // printf
#include <sttring.h>   // strcpy
#include "test.h"

// Test クラスの実装

void Test::Show() {
    Set();
    printf("%s\n", text);
}
D
void Test::Set() {
    strcpy(text, "TestのSet関数");
}

// XTest クラスの実装
D
void XTest::Set() {
    strcpy(text, "XTestのSet関数");
}

/*** ソースファイル : main.cpp ***/
#include <conio.h>    // getch
#include "test.h"

void samp() {
    Test t;      // Testクラス
    t.Show();
    XTest x;    // XTestクラス
    x.Show();
}

int main() {
    samp();
    if (getch()) return 0;
} 
【@継承(派生)】
 クラスは、要素を他のクラスに引き継がせる ( = クラスの継承 ) ことができます。一般に、継承元を 基底クラス、継承先を 派生クラスと呼びます。継承をするには、通例、派生クラスの宣言時に、" public : 基底クラス名 " を記述します。

【Aメンバ】
 派生クラスは、基底クラスの protected / public 宣言のメンバを自動的に受け継ぎます。したがってあらためてこれらを定義しなくても、自クラスのメンバとして使用することができます。
 サンプルでは Test クラスのメンバ変数 text とメンバ関数の Show() がこれに当たります。

【Bコンストラクタ】
 派生クラスは、インスタンス(実体)が作成されると、まず、基底クラスのコンストラクタを呼び出し、次に派生クラスのコンストラクタを呼び出します。

【Cデストラクタ】
 派生クラスが破棄される時は、まず派生クラスのデストラクタが実行され、次に基底クラスのデストラクタが呼び出されます。
作成破棄
継承元のコンストラクタ継承先のデストラクタ
継承先のコンストラクタ継承元のデストラクタ
【Dオーバーライド】
 派生クラスでは、基底クラスと同じ関数名で関数を定義 ( = オーバーライド ) することができます。サンプルでは Set() 関数が再定義されています。
 ただし、関数の呼び出しはクラス単位で行われますので、Show 関数内の Set 関数は常に、基底クラス ( Test クラス ) のものが呼ばれます。サンプルでは、派生クラスで Show関数を利用しても、派生クラスの Set関数が呼ばれず、基底クラスの Set関数が呼ばれています。
( main関数の出力を参照 / 仮想関数は例外 )
[ main関数の出力結果 ]
Test Start
TestのSet関数
Test Start
XTest Start
TestのSet関数
XTest End
Test End
Test End
( t のコンストラクタ)
( t のShow関数)
( x のコンストラクタ)
( x のコンストラクタ)
( x のShow関数)
( x のデストラクタ)
( x のデストラクタ)
( t のデストラクタ)
/*** test.h ***/
#include <stdio.h>     // printf 

// 基底クラス Test の宣言
class Test
{
protected:
    char str[20];
public:
    Test(){};
    virtual ~Test(){};    E
    void Show();
    virtual void Set();  EF
};

// 派生クラス XTest の宣言
class XTest : public Test
{
public:
    XTest(){};
    ~XTest(){};
    void Set();    E
};

・・・ 実装は省略 / 上記参照 ・・・

/*** main.cpp ***/
#include <conio.h>    // getch
#include "test.h"

int main()
{
    Test t;
    t.Show();

    XTest x;
    x.Show();

    if (getch()) return 0;
} 
【E仮想関数】
 基底クラスの関数に " virtual " をつけて宣言すると、その関数は 仮想関数 となります。仮想関数のオーバーライドは、オブジェクト単位での関数呼出を実現します。
 サンプルでは、基底クラス (Test) の Show 関数から呼び出される Set関数は、通常は基底クラスの Set関数となりますが、インスタンスが 派生クラス ( XTest) でかつ Show関数が仮想関数であれば、派生クラスの Set関数が呼び出されることになります。( main関数の出力結果を参照 )

* 特に、継承が想定されるクラスのデストラクタは、派生クラスのデストラクタが確実に呼ばれるようにするため、通常は virtual 宣言をします。

【F抽象クラス】
 仮想関数の末尾に " = 0 " をつけると 純粋仮想関数 になります。サンプルでは、基底クラスの Set関数を、

 virtual void Set() = 0;

と宣言すると純粋仮想関数となります。純粋仮想関数は、派生クラスで必ずオーバーライドしなければなりません。
 また、純粋仮想関数を持つクラスを 抽象クラス と呼び、このクラスはインスタンス(実体)をつくることができなくなります。したがって、サンプルでの main 関数内の、" Test t; " の記述はエラーとなります。
[ main関数の出力結果 ]
TestのSet関数
XTestのSet関数
( t のShow関数)
( x のShow関数)

●動的インスタンス

/*** test.h ヘッダファイル ***/
#include <stdio.h>     // printf

// 基底クラス Test の宣言
class Test
{
public:
    Test(){ printf("Test Start\n"); };
    Test(int n){ printf("Test2 Start\n");};
   ~Test(){ printf("Test End\n"); };
};

// 派生クラス XTest の宣言
class XTest : public Test
{
public:
    XTest(){ printf("XTest Start\n");};
    ~XTest(){ printf("XTest End\n"); };
};

/*** main.cpp ソースファイル ***/
#include <conio.h>    // getch
#include "test.h"

// main 関数
int main()
{
    Test *t;
    t = new Test(1);   // Testクラス
    delete t;

    XTest *x;
    x = new XTest;     // XTestクラス
    delete x;

    Test *a;
    a = new Test[5];   // Testの配列
    delete [] a;

    if (getch()) return 0;
}
【new】
 クラスは、new演算子を利用して動的にオブジェクトを作成することができます。
 また、new を使用するとコンストラクタが自動的に呼ばれます。この時、通常では、コンストラクタは引数があっても呼び出すことができますが、配列の場合は、オーバーロード ( 多重定義 ) されていても、引数なしのもの( = 既定のコンストラクタ)しか呼ぶことができません。

【delete】
 new で動的に作成したオブジェクトは、明示的に破棄しない限りメモリ上に残ります。使い終わったら、必ず delete演算子で破棄しなければなりません。特に、配列の delete には [ ] の記述が必要です。
 なお、delete を使用するとデストラクタが自動的に呼ばれます。
[ main関数の出力結果 ]
Test2 Start
Test End
Test Start
XTest Start
XTest End
Test End
Test Start
Test Start
Test Start
Test Start
Test Start
Test End
Test End
Test End
Test End
Test End
( t のコンストラクタ)
( t のデストラクタ)
( x のコンストラクタ)
( x のコンストラクタ)
( x のデストラクタ)
( x のデストラクタ)
( a[0] のコンストラクタ)
( a[1] のコンストラクタ)
( a[2] のコンストラクタ)
( a[3] のコンストラクタ)
( a[4] のコンストラクタ)
( a[4] のデストラクタ)
( a[3] のデストラクタ)
( a[2] のデストラクタ)
( a[1] のデストラクタ)
( a[0] のデストラクタ)

www.sasaraan.net

(c) morijoh