第十三回-01 テレビクラスを作ってみよう

さて、前回の講義でクラスとは「新たな型である」そして 「クラスとは「もの」である」という考え方を紹介した。

さらに、クラスとは「属性や状態 (データメンバ)」と
属性や状態に対する処理 (メンバ関数)」の2つの要素から構成されると解説した。

理屈ばかりこねていてもしょうがないので、今日はとにかく実際にクラスを記述してみよう。

テレビクラスを実現したプログラム

それでは、本ページでは前回紹介したテレビクラスを書いてみることにしよう。

テレビクラスとは以下のようなクラス図で書けるものであった。

テレビクラス
電源の状態
現在のチャンネル
現在の音量
電源ON/OFF
チャンネル変更
音量調節

これだけでは利用上機能が少し足りないので、以下のようにメンバ関数を追加してみることにしよう。

テレビクラス
電源の状態
現在のチャンネル
現在の音量
現在の電源状態取得
現在のチャンネル状態取得
現在の音量状態取得

電源ON/OFF
チャンネル変更
音量調節

現在の状態をコンソール表示

これをプログラムとして記述していくわけだが、本講義は今回が最終回である。
細かな文法を解説して皆さんがクラスを自分で書けるようになるまで教えるには時間が足りない。
そこで、こちらで記述済みのテレビクラスを提供し、皆さんにオブジェクト指向プログラミングを体験してもらい講義を終えることにしよう。

テレビクラスを完成させたプログラム全体は以下である。
#include <iostream>
using namespace std;

class Television
{
	// データメンバ
	int power;
	int channel;
	int volume;

public:
	Television();
	~Television();
	
	// 状態取得用のメンバ関数
	int getPower();
	int getChannel();
	int getVolume();

	// 状態設定用のメンバ関数
	void setPower(int p);
	void setChannel(int c);
	void setVolume(int v);

	// 状態表示用のメンバ関数
	void printStatus();
};

Television::Television()
{
	power = 0;   // 電源オフ
	channel = 1; // チャンネル1。首都圏では NHK
	volume = 0;  // ボリューム0
}

Television::~Television()
{
}

// 値の取得用関数 (getter) 3つ
int Television::getPower()
{
	return power;
}

int Television::getChannel()
{
	return channel;
}

int Television::getVolume()
{
	return volume;
}

// 値の設定用関数 (setter) 3つ
void Television::setPower(int p)
{
	power = p;
}

void Television::setChannel(int c)
{
	channel = c;
}

void Television::setVolume(int v)
{
	volume = v;
}

void Television::printStatus()
{
	if(power == 1)
	{
		cout << "電源はオンです。";
	}
	else
	{
		cout << "電源はオフです。";
	}

	cout << "チャンネルは" << channel << "です。";

	cout << "ボリュームは" << volume << "です。\n";
}

int main(void)
{

	Television tv;  // Television クラスの変数 tv を宣言
					// クラスを「新しい型」として用いている
					// この時点でコンストラクタが呼ばれる

	tv.printStatus(); // 確認のために状態表示

	tv.setPower(1);    // 電源オン
	tv.setChannel(8);  // 8チャンネルにセット
	tv.setVolume(10);  // ボリュームを10に

	tv.printStatus();

	tv.setChannel(4);  // 4チャンネルにセット

	tv.printStatus();
}
今回も、paiza.IO のプログラムを全て差し替える形となっている。
すなわち、まず paiza.IO のデフォルト状態のプログラム記述欄でキーボードで「Ctrl-a」を実行し、プログラム全体を選択する。
そして、キーボードのDeleteキーを押して、プログラム全体を削除する。



その結果、もちろん以下のようにプログラムが全く書かれていない状態になる。



その状態で、上で提示したプログラムをマウスで選択し、キーボードの Ctrl-c によりコピーする。
その後、何も書かれていない paiza.IO にてキーボードで Ctrl-v を実行してプログラム全体を貼り付けるのである。

貼り付けたらまずは実行してみよう。以下のような出力が現れるはずである。
電源はオフです。チャンネルは1です。ボリュームは0です。
電源はオンです。チャンネルは8です。ボリュームは10です。
電源はオンです。チャンネルは4です。ボリュームは10です。
この出力の意味を理解できるよう以下で解説を行う。


プログラムの中身 (1) テレビクラスの宣言

まずは、上で提示したプログラムのうち、クラスの宣言を行っている部分を見てみよう。
それが、下記の部分である。
class Television
{
	// データメンバ
	int power;
	int channel;
	int volume;

public:
	Television();
	~Television();
	
	// 状態取得用のメンバ関数
	int getPower();
	int getChannel();
	int getVolume();

	// 状態設定用のメンバ関数
	void setPower(int p);
	void setChannel(int c);
	void setVolume(int v);

	// 状態表示用のメンバ関数
	void printStatus();
};
まず、冒頭に「class Television」と書かれていることから、テレビクラスには「Television」というクラス名が付けられていることがわかる。

また、注意深く見ると、Televisionクラスの中身は以下のテレビクラスのクラス図をほぼプログラムの言葉に翻訳したものであることがわかる。
すなわち、「電源の状態」が変数 power に、「現在のチャンネル」が変数 channel に、「現在の音量」が変数 volume に対応する。
他にも、「現在の電源状態取得」が関数 getPower() に、「電源ON/OFF」が関数 setPower(int p) に対応することなどがイメージできれば良い。

このように、クラス図を元にクラスの宣言を書くところからプログラミングが始まる。

テレビクラス
電源の状態
現在のチャンネル
現在の音量
現在の電源状態取得
現在のチャンネル状態取得
現在の音量状態取得

電源ON/OFF
チャンネル変更
音量調節

現在の状態をコンソール表示

なお、上のクラスの宣言には、クラス図にない以下の関数がある。これは、オブジェクト指向プログラミングでは以下の名前と意味がある重要なものである。
が、今日はあまり深入りしない。

メンバ関数名 名前 説明
Television() コンストラクタ オブジェクトが生成されるときに自動的に呼ばれる。クラス名と同じ名前で戻り値を持たない特別な関数。
~Television() デストラクタ オブジェクトが破棄ときに自動的に呼ばれる。クラス名に ~ をつけた名前で戻り値を持たない特別な関数。



プログラムの中身 (2) Televisionクラスの実装

クラスの宣言に合わせて、メンバ関数の実装を行う必要がある。
第十回で関数を学んだ際、関数の宣言(プロトタイプ)と実装の両方が必要だったことと似ている。

本ページ冒頭で提示したプログラムのうち、Televisionクラスの実装を行っているのが以下の部分である。
Television::Television()
{
	power = 0;   // 電源オフ
	channel = 1; // チャンネル1。首都圏では NHK
	volume = 0;  // ボリューム0
}

Television::~Television()
{
}

// 値の取得用関数 (getter) 3つ
int Television::getPower()
{
	return power;
}

int Television::getChannel()
{
	return channel;
}

int Television::getVolume()
{
	return volume;
}

// 値の設定用関数 (setter) 3つ
void Television::setPower(int p)
{
	power = p;
}

void Television::setChannel(int c)
{
	channel = c;
}

void Television::setVolume(int v)
{
	volume = v;
}

void Television::printStatus()
{
	if(power == 1)
	{
		cout << "電源はオンです。";
	}
	else
	{
		cout << "電源はオフです。";
	}

	cout << "チャンネルは" << channel << "です。";

	cout << "ボリュームは" << volume << "です。\n";
}
このうち、以下のコンストラクタ内では初期状態として「電源オフ (power=0;)」、「チャンネル (channel=1;)」、「ボリューム (volume=0;)」で初期化している。
power、channel、volume は整数で定義しているため、電源は 0 がオフ、1 がオンを意味するように決めていることにも注意して欲しい。
Television::Television()
{
	power = 0;   // 電源オフ
	channel = 1; // チャンネル1。首都圏では NHK
	volume = 0;  // ボリューム0
}
この初期化が何を意味するかというと、プログラムで初めてTelevisionクラスを使う際、電源がオフ、チャンネルが1、ボリュームが0の状態にしておく、ということである。
本物のテレビに例えて言えば、テレビを購入した直後の状態が「電源がオフ、チャンネルが1、ボリュームが0の状態」、ということに対応する。

コンストラクタとデストラクタの後ろに記述されている値の取得用関数 (getPower, getChannel, getVolume) は単にデータメンバの値を返しているだけ
値の設定用関数 (setPower, setChannel, setVolume) は単に引数の値をデータメンバにセットしているだけである。
これらのことをそれぞれゲッター (getter)、セッター (setter) という。

これらは短い関数ではあるが、Televisionクラスとしての意味は重要である。
例えば、setPower(1) を実行すると、変数 power に 1 が格納される。これは、テレビの電源がオンになることを意味する。
逆に、setPower(0) なら変数 power に 0 が格納され、テレビの電源がオフになる。

クラスを用いたプログラミングでは、このように小さな機能のメンバ関数をたくさん記述することが多い。

最後に記述されているのは、Televisionクラスの現在の状態を表示するメンバ関数 printStatus である。
この関数は、課題で取り扱う内容なので良く理解して欲しい
といっても、皆さんの知識で理解できる内容しか記していないので安心して欲しい (でないと課題が解けない)。
void Television::printStatus(){

	if(power == 1)
	{
		cout << "電源はオンです。";
	}
	else
	{
		cout << "電源はオフです。";
	}

	cout << "チャンネルは" << channel << "です。";

	cout << "ボリュームは" << volume << "です。\n";

}
何をしているかというと、現在の Television クラスの状態、すなわち変数 power、channel、volume の状態をコンソールに表示するのである。
power の場合、1 がオン、0 がオフに対応するので、if 文でオンかオフかを日本語で表示するようにしている。
channel と volume は整数値を表示するだけである。

冒頭でプログラムを実行したとき、以下の出力が得られた。これはこの printStatus 関数を3回実行した結果であることがイメージできるだろうか。
電源はオフです。チャンネルは1です。ボリュームは0です。
電源はオンです。チャンネルは8です。ボリュームは10です。
電源はオンです。チャンネルは4です。ボリュームは10です。
では、 printStatus 関数はどこで3回実行されているのだろうか。
それは、main 関数内部である。


プログラムの中身 (3) main 関数

冒頭のプログラムのmain関数は、長いプログラムの末尾に記されていた。以下の部分である。
int main(void)
{

	Television tv;  // Television クラスの変数 tv を宣言
					// クラスを「新しい型」として用いている
					// この時点でコンストラクタが呼ばれる

	tv.printStatus(); // 確認のために状態表示

	tv.setPower(1);    // 電源オン
	tv.setChannel(8);  // 8チャンネルにセット
	tv.setVolume(10);  // ボリュームを10に

	tv.printStatus();

	tv.setChannel(4);  // 4チャンネルにセット

	tv.printStatus();
}
オブジェクト指向プログラミングでは main 関数は作成したクラスを動作させるために使われることが多い。

冒頭の「Television tv;」は、記述された Television クラスを新たな型として利用し、変数 tv を宣言している

つまり、第十二回-02で述べたように、クラスは新たな型のことである、というわけである。
なお、この変数 tv のことを、Television クラスのインスタンス、とも呼ぶ。

この変数 tv に対して、以下の処理が順に行われている。
メンバ関数の呼び出しには、変数 tv にドット演算子 ( . ) を用いて tv.printStatus() などとする必要があることにも注意。
  1. 状態表示 (tv.printStatus();)
  2. 電源オン (tv.setPower(1);)
  3. 8チャンネルにセット (tv.setChannel(8);)
  4. ボリューム10にセット (tv.setVolume(10);)
  5. 状態表示 (tv.printStatus();)
  6. 4チャンネルにセット (tv.setChannel(4);)
  7. 状態表示 (tv.printStatus();)
状態表示 (tv.printStatus();) が3回呼ばれていることがわかるだろうか? それが、プログラム実行時に下記の出力が表示された理由である。
電源はオフです。チャンネルは1です。ボリュームは0です。
電源はオンです。チャンネルは8です。ボリュームは10です。
電源はオンです。チャンネルは4です。ボリュームは10です。
以上を見ると、main 関数で Television クラスの変数 tv を操作することは、本物のテレビをリモコン操作することに似ていることに気づくだろう。
これは、第十二回-02で述べた、クラスとはものである、という考え方に関連している。



何故クラスを使うのか?→

オンラインコンパイラで C/C++ を自習しように戻る