C++入門|クラス内のメンバ利用

外から使うときは warrior.power、クラスの中では power。メンバの呼び出し方を覚えれば、オブジェクトが自分の力で動き出します。

C++のオブジェクト指向では、クラスからインスタンスを作り、そのインスタンスが持つメンバ変数やメンバ関数を使って処理を行います。

前回は、Warriorクラスを作り、戦闘力を表すpowerと、修行する動作を表すtrainを用意しました。

今回は、その続きとして、メンバをどのように使うのかを詳しく見ていきます。

メンバとは、クラスの中にあるメンバ変数とメンバ関数の総称です。

種類Warriorクラスでの例意味
メンバ変数power戦士の戦闘力
メンバ関数train(double hour)指定した時間だけ修行する

C++では、インスタンスからメンバを使うときに、ドット演算子 . を使います。

たとえば、warriorというインスタンスがある場合、powerに値を入れるには次のように書きます。

warrior.power = 80;

また、trainというメンバ関数を呼び出すには、次のように書きます。

warrior.train(1.5);

ドラゴンボール風にたとえるなら、warriorは実際に修行場にいる戦士です。
warrior.power = 80; は、その戦士の戦闘力カプセルに80を設定する命令です。
warrior.train(1.5); は、その戦士に1.5時間の修行を指示する命令です。

つまり、ドット演算子は「この戦士の、この能力や動作を使う」という指定をするための記号です。

図:ドット演算子でメンバ変数にアクセスする

この図が示していること

この図では、warriorというインスタンスの中にあるpowerメンバ変数へ、ドット演算子を使ってアクセスする流れを表しています。

warrior.power = 80; と書くことで、warriorオブジェクトが持っているpowerに80を代入できます。

クラスの外側からメンバを使うときは、変数名.メンバ名という形で書くのが基本です。

前回のWarriorクラスを確認する

まず、前回作成したWarriorクラスの構成を確認しましょう。

Warriorクラスは、次の3つのファイルで構成されています。

ファイル役割
warrior.hWarriorクラスの宣言を書く
warrior.cpptrainメンバ関数の定義を書く
main.cppインスタンスを生成してメンバを使う

このうち、今回特に注目するのは、main.cppでのメンバアクセスと、warrior.cppでのメンバ関数内の処理です。

Warriorクラスの宣言

Warriorクラスには、メンバ変数powerと、メンバ関数trainがあります。

プロジェクト/ファイル名: Chap3_01/warrior.h

#ifndef _WARRIOR_H_
#define _WARRIOR_H_

class Warrior {
public:
    // 戦闘力
    double power;

    // 修行する
    void train(double hour);
};

#endif // _WARRIOR_H_

このクラス宣言を見ると、Warriorクラスの中にpowerとtrainがあることが分かります。

double power;

これは、戦士の戦闘力を表すメンバ変数です。

void train(double hour);

これは、指定した時間だけ修行するメンバ関数です。

また、これらはpublicの中に書かれています。

public:

publicの中にあるメンバは、クラスの外側からもアクセスできます。
そのため、main.cppからwarrior.powerやwarrior.train(1.5)のように使えるわけです。

メンバ変数へのアクセス

main.cppでは、Warriorクラスからwarriorというインスタンスを作ります。

そのあと、warriorのpowerに値を代入します。

プロジェクト/ファイル名: Chap3_01/main.cpp

#include <iostream>
#include "warrior.h"

using namespace std;

int main(int argc, char** argv) {
    // インスタンスを生成する
    Warrior warrior;

    // 戦闘力を設定する
    warrior.power = 80;

    // 指定した時間だけ修行する
    warrior.train(1.5);

    // プログラムが正常に終わったことを示す
    return 0;
}

ここで注目したいのは、次の行です。

warrior.power = 80;

これは、warriorというインスタンスの中にあるpowerメンバ変数に80を代入しています。

書き方意味
warriorWarriorクラスから作られたインスタンス
powerwarriorが持つメンバ変数
warrior.powerwarriorのpowerを指定する
warrior.power = 80;warriorの戦闘力を80にする

ドット演算子 . は、インスタンスの中にあるメンバを指定するために使います。

ドラゴンボール風にいえば、warriorという戦士の戦闘力カプセルに80を入れているようなイメージです。

メンバ関数へのアクセス

次に、メンバ関数を呼び出す部分を見てみましょう。

warrior.train(1.5);

これは、warriorというインスタンスのtrainメンバ関数を呼び出しています。

書き方意味
warrior対象となるインスタンス
train呼び出すメンバ関数
1.5train関数へ渡す引数
warrior.train(1.5);warriorに1.5時間修行させる

この処理により、warrior.cppに定義されているtrain関数が実行されます。

実行すると、次のような結果になります。

戦闘力80の戦士が1.5時間修行
120ポイント成長しました。

ここでは、powerが80、hourが1.5なので、80 × 1.5 = 120 となります。

図:ドット演算子でメンバ関数を呼び出す

この図が示していること

この図では、warrior.train(1.5);によって、warriorオブジェクトのtrainメンバ関数が呼び出される流れを表しています。

train関数の中では、warriorが持つpowerと、引数として渡されたhourを使って、成長ポイントを計算します。

クラスの外側からメンバ関数を呼び出すときは、変数名.メンバ関数名という形で書きます。

メンバを呼び出す基本形

メンバ変数でも、メンバ関数でも、クラスの外側からアクセスするときは基本形が同じです。

変数名.メンバ名

たとえば、warriorというインスタンスのpowerを使うなら、次のように書きます。

warrior.power

warriorというインスタンスのtrainを呼び出すなら、次のように書きます。

warrior.train(1.5)
使いたいもの書き方内容
メンバ変数warrior.powerwarriorのpowerを使う
メンバ関数warrior.train(1.5)warriorのtrainを呼び出す

この「変数名.メンバ名」という形は、C++のオブジェクト指向でとてもよく使います。

ドット演算子を見るときは、「このオブジェクトの中にあるものを使っている」と考えると分かりやすいです。

warrior.cppのtrain関数を見てみよう

次に、メンバ関数の中身を見てみましょう。

プロジェクト/ファイル名: Chap3_01/warrior.cpp

#include "warrior.h"
#include <iostream>

using namespace std;

// 修行する
void Warrior::train(double hour) {
    cout << "戦闘力" << power << "の戦士が" << hour << "時間修行" << endl;
    cout << power * hour << "ポイント成長しました。" << endl;
}

ここで注目したいのは、train関数の中でpowerを使っている部分です。

cout << "戦闘力" << power << "の戦士が" << hour << "時間修行" << endl;
cout << power * hour << "ポイント成長しました。" << endl;

main.cppでは、powerにアクセスするときにwarrior.powerと書いていました。

warrior.power = 80;

しかし、train関数の中では、単にpowerと書いています。

power

これは、train関数がWarriorクラスのメンバ関数だからです。

同じクラスの中にあるメンバ変数は、メンバ関数の中からそのままの名前で使えます。

クラス外から使う場合とクラス内で使う場合の違い

ここが今回のいちばん大切なポイントです。

クラスの外側からメンバを使う場合は、どのインスタンスのメンバなのかを明示する必要があります。

そのため、main.cppでは次のように書きます。

warrior.power = 80;
warrior.train(1.5);

一方、Warriorクラスのメンバ関数の中では、同じクラスのメンバ変数powerをそのまま使えます。

power * hour

この違いを表にすると、次のようになります。

場所書き方理由
main.cppなどクラスの外側warrior.powerどのインスタンスのpowerかを指定する必要がある
main.cppなどクラスの外側warrior.train(1.5)どのインスタンスのtrainかを指定する必要がある
Warrior::trainの中power同じクラス内のメンバ変数なのでそのまま使える
Warrior::trainの中hourtrain関数の引数なのでそのまま使える

ドラゴンボール風にたとえるなら、main.cppは修行場の外から戦士へ命令している立場です。
そのため、「warriorという戦士のpower」「warriorという戦士のtrain」と、対象をはっきり指定する必要があります。

一方、train関数の中は、warrior自身の内部処理です。
自分自身のpowerを使うので、わざわざwarrior.powerとは書かず、powerだけで通じます。

hourはメンバ変数ではなく引数

train関数では、powerとhourが登場します。

void Warrior::train(double hour)

ここでhourは、train関数に渡される引数です。
Warriorクラスのメンバ変数ではありません。

名前種類内容
powerメンバ変数Warriorオブジェクトが持つ戦闘力
hour引数train関数を呼び出すときに渡される修行時間

main.cppで次のように呼び出すと、

warrior.train(1.5);

1.5がhourに渡されます。

そのため、train関数の中では、hourの値は1.5になります。

cout << "戦闘力" << power << "の戦士が" << hour << "時間修行" << endl;

powerはwarriorが持っている戦闘力です。
hourはtrain関数に渡された修行時間です。

この2つを使って、成長ポイントを計算しています。

power * hour

warrior.powerが80で、hourが1.5なら、計算結果は120になります。

メンバ関数の定義で使うWarrior::train

メンバ関数をクラスの外側で定義するときは、関数名の前にクラス名を付けます。

void Warrior::train(double hour)

これは、Warriorクラスのtrain関数を定義しているという意味です。

書き方意味
train関数名
Warrior::trainWarriorクラスに属するtrain関数
::所属を示すスコープ解決演算子

もし単にtrainと書くだけでは、どのクラスのtrainなのか分かりません。

Warrior::trainと書くことで、Warriorクラスのメンバ関数であることを明確にしています。

ドラゴンボール風にいうなら、trainという修行メニューが、Warrior設計図に属していることを示す流派札のようなものです。

クラス内ではメンバ変数をそのまま使える

Warrior::trainの中では、powerをそのまま使っています。

cout << "戦闘力" << power << "の戦士が" << hour << "時間修行" << endl;

これは、powerがWarriorクラスのメンバ変数だからです。

同じクラス内のメンバ関数から見れば、powerは自分の中にあるデータです。
そのため、ドット演算子を使わず、そのままpowerと書けます。

cout << power * hour << "ポイント成長しました。" << endl;

この計算も、同じクラス内のpowerを使っています。

場所powerの書き方
main.cppwarrior.power
warrior.cppのWarrior::train内power

この違いを意識できると、クラスの外側と内側の見方がかなり分かりやすくなります。

図:クラス内ではメンバ変数をそのまま使える

この図が示していること

この図では、クラスの外側と内側でメンバ変数の書き方が変わることを表しています。

main.cppのようにクラスの外側から使う場合は、warrior.powerのようにインスタンス名を付けます。
一方、Warrior::trainの中では、同じクラス内のメンバ変数なので、powerとそのまま書けます。

また、hourはメンバ変数ではなく、train関数に渡された引数です。
powerとhourを使って、power * hourの計算を行っています。

メンバ利用の流れを順番に確認する

今回のプログラムの流れを、もう一度順番に見てみましょう。

まず、main.cppでWarriorクラスのインスタンスを作ります。

Warrior warrior;

これにより、warriorという実際の戦士オブジェクトができます。

次に、warriorのpowerに80を代入します。

warrior.power = 80;

この時点で、warriorの戦闘力は80になります。

次に、trainメンバ関数を呼び出します。

warrior.train(1.5);

この呼び出しによって、Warrior::train(double hour)が実行されます。

train関数の中では、powerとhourを使います。

power * hour

powerはwarriorが持っているメンバ変数です。
hourはtrain(1.5)で渡された引数です。

そのため、80 × 1.5 = 120となり、120ポイント成長したと表示されます。

手順処理内容
1Warrior warrior;インスタンスを生成する
2warrior.power = 80;メンバ変数powerに80を代入する
3warrior.train(1.5);メンバ関数trainを呼び出す
4power * hour80 × 1.5を計算する
5coutで表示修行結果を表示する

ドット演算子を使う場面

ドット演算子は、インスタンスの中にあるメンバへアクセスするときに使います。

インスタンス名.メンバ名

今回のサンプルでは、次の2つが登場しました。

warrior.power
warrior.train(1.5)
書き方使っているもの内容
warrior.powerメンバ変数warriorの戦闘力
warrior.train(1.5)メンバ関数warriorに1.5時間修行させる

ドット演算子を見るときは、「どのオブジェクトのメンバを使っているのか」を読むのが大切です。

warrior.powerなら、warriorのpowerです。
別のインスタンスfighterがあれば、fighter.powerはfighterのpowerになります。

つまり、同じpowerという名前でも、どのインスタンスのpowerかによって別のデータになります。

クラス内のメンバ利用で押さえたいポイント

クラス内のメンバ利用では、次のポイントを押さえておきましょう。

ポイント内容
メンバ変数へアクセスインスタンス名.メンバ変数名
メンバ関数を呼び出すインスタンス名.メンバ関数名(引数)
クラス内でメンバ変数を使うメンバ変数名だけで使える
クラス外でメンバを使うどのインスタンスのメンバかを指定する
メンバ関数の定義クラス名::メンバ関数名で書く

ドラゴンボール風に言えば、main.cppは外から戦士に指示を出す司令室です。
そのため、warrior.powerやwarrior.train(1.5)のように、対象の戦士をはっきり指定します。

一方、Warrior::trainの中は、戦士自身の内部処理です。
自分のpowerを使うので、powerとそのまま書けます。

クラスの外と中を区別して読む

C++のクラスを読むときは、「今どこからメンバを使っているのか」を意識すると分かりやすくなります。

main.cppのようにクラスの外側から使っているなら、インスタンス名.メンバ名になります。

warrior.power = 80;
warrior.train(1.5);

warrior.cppのWarrior::trainの中なら、同じクラスのメンバ変数はそのまま使えます。

power * hour

この違いが分かると、クラスを使ったC++プログラムがぐっと読みやすくなります。

オブジェクト指向では、オブジェクトが自分のデータを持ち、自分の動作を実行します。
warriorはpowerを持ち、trainで修行します。
外からはwarrior.powerやwarrior.train(1.5)で操作し、内部ではpowerを使って処理を進めます。

この感覚をつかめると、C++のクラスとオブジェクトの動きがかなり自然に見えてきます。