クラスの基礎

シンプルなクラス

まずはごく簡単なクラスのサンプルコードを示します。


#include <iostream>
#include <string>

//クラス「SimpleClass」を定義
class SimpleClass
{
public:
    int number;
    std::string name;
};

int main()
{
	//定義したクラスの変数(インスタンス)を宣言
    SimpleClass sc;
    sc.number = 0;
    sc.name = "〇山×夫";

    std::cout << "number: " << sc.number;
    std::cout << "\nname: " << sc.name << std::endl;

    std::cin.get();
}

5~10行目がクラスの定義です。
クラスはclassというキーワードで定義します。
クラスの最後は;(セミコロン)で終了します。

クラスというのは構造体によく似ています。
実際にmain関数内の処理だけを見ると、まるで構造体を使用しているように見えます。

C++では構造体の機能も拡張され、クラスとほぼ同じように使用することができます。
違いは、後述するアクセス修飾子(および継承時のアクセス修飾子)のデフォルト値がクラスはprivate、構造体はpublicであることくらいです。

アクセス修飾子

class内のコードの最初にpublic:というキーワードがあります。
これはアクセス修飾子といいます。
(最後の:(コロン)を忘れないように)

アクセス修飾子はクラスのカプセル化のために必要な機能です。
public:を付けて宣言されたメンバ変数(クラス内変数)は、外部から自由にアクセスが可能、という意味になります。

アクセス修飾子には以下があります。

private
そのクラス内からのみアクセスが可能。
何も指定しない場合はprivateになる。
(デフォルト)
public
どこからでもアクセスが可能。
protected
そのクラスと、そのクラスを継承したクラスからのみアクセスが可能。

protectedは継承という機能に関係するアクセス修飾子です。
これについては継承の項で説明します。

アクセス修飾子は以下のように記述します。


class SimpleClass
{
private: //ここからprivate
    int a;
    int b;
	//デフォルトはprivateなので
	//最初の「private:」は書かなくても同じ

public: //ここからpublic
    int c;
    int d;

protected: //ここからprotected
    int e;
    int f;

private: //ここからprivate
    int g;
    int h;
};

クラス内にアクセス修飾子を書くと、そこから先のメンバ変数はそのアクセス制限の変数となります。
新たなアクセス修飾子を書くと、そこからはまた別のアクセス制限の変数となります。
アクセス修飾子は何度でも書けますが、同じアクセス制限の変数はまとめてしまった方が見やすいでしょう。

アクセス修飾子を付けない場合、自動的にprivateが指定されたものとみなされます。
そのため、明示的にpublicを指定しないと外部からクラスのメンバ変数にアクセスが出来なくなります。


#include <iostream>
#include <string>

//クラス「SimpleClass」を定義
class SimpleClass
{
    //privateとみなされる
    int number;
public: //ここからpublic
    std::string name;
};

int main()
{
	//定義したクラス型変数(インスタンス)を宣言
    SimpleClass sc;

    //アクセスできない
    sc.number = 0;	//コンパイルエラー
    //アクセスできる
    sc.name = "〇山×夫";
}

インスタンス

main関数内では定義したクラス型の変数を宣言し、使用しています。
クラス型の変数は、インスタンスという呼び方をされることもあります。
(クラスのオブジェクトという呼び方もあります)

クラスというのは物の設計図のようなものです。
設計図を書いても、実際に製品を作らなければ使うことはできません。
クラス定義を元に「実体」を作ることをインスタンス化するといいます。
インスタンスは必要なだけいくつでも作ることができます。

何か特別な名前がついていますが、難しく考える必要はありません。
構造体を使用するために構造体変数を宣言するのと同じようなもの、と考えると良いです。

インスタンスは「実体」という意味なので(instance=実体)、インスタンスをポインタで扱う場合は「ポインタ変数=インスタンス」ではありません。
実体はポインタが指し示す先にあります。

メンバ関数

クラスは内部に変数を持つことができますが、関数を持つこともできます。
これをメンバ関数と言います。
(クラス内変数をメンバ変数と呼ぶのと同じです)


#include <iostream>
#include <string>

class SimpleClass
{
private:
    int number;
    std::string name;

public:
    int GetNumber()
    {
        return number;
    }
    void SetNumber(int n)
    {
        number = n;
    }

    std::string GetName()
    {
        return name;
    }
    void SetName(const char* s)
    {
        name = s;
    }
};

int main()
{
    SimpleClass sc;
    sc.SetNumber(0);
    sc.SetName("〇山×夫");

    //メンバ変数はすべてprivateなので
    //こういう方法ではアクセスできなくなっている
    //sc.number = 0;

    std::cout << "number: " << sc.GetNumber();
    std::cout << "\nname: " << sc.GetName() << std::endl;

    std::cin.get();
}

先ほどのサンプルコードのクラスのメンバ変数をすべてprivateに変更しています。

アクセッサ

メンバ変数をすべてprivateにしたので、sc.numberといった方法では外部からアクセスできなくなりました。
その代わりに、メンバ関数をpublicで定義して、そこからメンバ変数にアクセスできるようにしています。
privateメンバはクラス外部からアクセスできませんが、クラス内部、つまりクラスのメンバ関数からはアクセス可能です。

このようなメンバ変数を読み書きするためのメンバ関数をアクセッサ(アクセサ)と言います。
特に、値を取得するためのアクセッサをgetter(ゲッター)、値を設定するためのアクセッサをsetter(セッター)と言います。

メンバ変数をprivateにすることで、外部からアクセスできなくなり情報の隠蔽ができたように見えます。
しかしこのコードは、隠蔽方法としては全く無意味です。
なぜならば、直接メンバ変数にアクセスできなくともアクセッサを通して値の読み書きが自由に行えるので、隠蔽したことにならないからです。
つまりメンバ変数をpublicで定義するのと何ら変わりません。

必要のない操作まで許可しないのが基本的な考え方です。
今回のようなサンプル程度では隠蔽化しようがないので、この辺のことはおいおい説明していきます。

this

クラス内のメンバ関数ではthisというキーワードを使用できます。
これはそのクラスを操作しているインスタンス自身のポインタです。


class SimpleClass
{
private:
    int number;

public:
    void SetNumber(int n)
    {
        number = n;
    }

    void ShowNumber(int number)
    {
        std::cout << "メンバ変数number: " << this->number << std::endl;
        std::cout << "引数number: " << number << std::endl;
    }
};

int main()
{
    SimpleClass sc1;
    sc1.SetNumber(10);
    sc1.ShowNumber(20);

	SimpleClass sc2;
    sc2.SetNumber(30);
    sc2.ShowNumber(40);
}
メンバ変数number: 10
引数number: 20
メンバ変数number: 30
引数number: 40

メンバ関数ShowNumber内で使用しているthisは、そのメンバ関数を呼び出したインスタンス自身のポインタとなります。
インスタンスsc1から呼び出した場合は、thissc1を指しています。
インスタンスsc2から呼び出した場合は、thissc2を指しています。
ポインタなので、アロー演算子(->)でそれぞれのインスタンスが保存しているメンバ変数にアクセスすることができます。

thisを使用せずとも、メンバ関数内ではメンバ変数にアクセスすることは可能です。
上記コードのShowNumber関数以外のメンバ関数では、thisを使用せずに直接メンバ変数を使用しています。

thisはメンバへのアクセスであることを明示的に指定する場合に使用します。
ShowNumber関数では、メンバ変数と引数とで同じnumberという名前を使用しています。
thisを使用しない場合は引数の方の名前が優先され、メンバ変数の方にはアクセスできなくなります。
thisを使用することで自分自身へのアクセスが可能になります。

上記のような場合では名前が重複しないように気を付ければthisは使用する必要はありませんが、他にもメンバ関数に自分自身を渡す場合にもthisは使用されます。


class SimpleClass
{
private:
    int number;

public:
    void SetNumber(int n)
    {
        number = n;
    }

    void ShowNumber()
    {
		//メンバ関数に自分自身を渡す
        ShowNumber(this);
    }
    void ShowNumber(const SimpleClass *sc) 
    {
        std::cout << "メンバ変数number: " << sc->number << std::endl;
    }
};

int main()
{
    SimpleClass sc;
    sc.SetNumber(10);

    //どちらも同じ
    sc.ShowNumber();
    sc.ShowNumber(&sc);
}

クラステンプレート

関数テンプレートでは、関数内で使用するデータ型を、関数呼び出し時に決定することができました。
(関数の新機能参照)
これと同じことがクラスでも可能です。


#include <iostream>
#include <string>

template<typename T>
class TestClass
{
    T value;

public:
    void Set(T val)
    {
        value = val;
    }

    T Get()
    {
        return value;
    }

    void Print()
    {
        std::cout << value << std::endl;
    }
};

int main()
{
    TestClass<int> tc1;
    tc1.Set(10);
    tc1.Print();

    TestClass<std::string> tc2;
    tc2.Set("ABC");
    tc2.Print();

    std::cin.get();
}
10
ABC

詳しい使い方はテンプレートの機能の項で改めて説明します。