木曜日, 1月 24, 2008

C++解説5(クラスについて2)

1. インスタンス変数/関数とクラス変数/関数

クラスとは型宣言である事は述べました。
しかし、そこはユーザー定義型のためいろいろな事ができるようになっています。
その中の一つにクラス変数とクラス関数というものがあります。

最初にインスタンスという言葉について触れておきましょう。
インスタンスとは「例えば」何て訳を学校で習った覚えがあるのは私だけではないでしょう。
普通、「例えば~」と説明する場合、~には具体的な例などを上げるのだと思います。
何となく近づいてきましたかね。
ここではインスタンスとは実態を指します。
int i;

という宣言文があった場合、iをインスタンスと言います。
classの宣言はintの宣言と言う事になります。
C++において、int,doubleと言った型はクラスではありません。
しかし、C++の仕様においてintと宣言した場合は32bitで表現可能範囲を示すとか、
unsigned intと記述された場合は最初に符号ビットを必要としないため、
intより+範囲に大きく数値を管理できるなど、すべて仕様として決められています。
この仕様を決めるのがクラスの定義となります。しかし、定義だけでは使用できません。
構造体でも定義だけではなく、実態を作成する宣言と言った事が必要となります。
この実態宣言されたものをインスタンスと言います。

class Human {
private:
char *Name;
int Age;
char Birthday[9];
public:
Human(char*, int, char*);
~Human();
};

とクラス定義があった場合に

Human obj1;
Human obj2;
と宣言されると、obj1のName,Age,Birthdayとobj2のName,Age,Birthdayは
全く別のものになります。もちろん、Human()も~Human()も同様です。
これらをインスタンス変数、インスタンス関数と表現します。

では一体、クラス変数、クラス関数とは何でしょうか?
これはインスタンスのものではなく、クラスのものという事になります。
C++ではこのような変数又は関数をstaticを付けて表現します。
たとえば、世の中の人口を管理すると仮定します。
そんな値を管理するのは何処が適しているでしょうか?
いろいろと分析方法は異なるため、何処が一番適しているという答えはありません。
しかし一つの答えに人間の事は全部人間のクラスで管理しろ!という考え方があっても良いでしょう。
ここではそれをクラス定義に盛り込み、実際に使用してます。

--------------------------------------------------------------------------------
class Human {
private:
static int HumanCount; // クラス変数
static void HumanCountAdd(); // クラス関数非公開
static void HumanCountSubtract(); // クラス関数非公開

char *Name;
int Age;
char Birthday[9];
public:
Human(char*, int, char*);
~Human();

static void HumanCountPrint(char*); // クラス関数公開
};

int Human::HumanCount = 0;

void Human::HumanCountAdd() {
HumanCount++;
}

void Human::HumanCountSubtract() {
HumanCount--;
}

void Human::HumanCountPrint(char* Title) {
std::cout << Title << ":HumanCount = " << HumanCount << "\n";
}

Human::Human(char *InName, int InAge, char *InBirthday) {
Name = new char(strlen(InName));
strcpy(Name, InName);
Age = InAge;
strcpy(Birthday, InBirthday);
// std::cout << "(" << Name << ") Human::Human()\n"; // ←今回は削除
Human::HumanCountAdd(); // ここにカウントアップを追加
}

Human::~Human() {
// std::cout << "(" << Name << ") Human::~Human()\n"; // ←今回は削除
delete Name;
Human::HumanCountSubtract(); // ここにカウントダウンを追加
}

int main(void) {
Human::HumanCountPrint("no create"); // この関数で現在の数をプリントします

Human obj1("semona", 0, "20040101");

Human::HumanCountPrint("obj1 create");

Human *obj2 = new Human("yuuna", 1, "20030101");

Human::HumanCountPrint("obj2 create");

delete obj2;

Human::HumanCountPrint("obj2 delete");
}
-------------------------------------------------------------------------------
実行結果
-------------------------------------------------------------------------------
no create:HumanCount = 0
obj1 create:HumanCount = 1
obj2 create:HumanCount = 2
obj2 delete:HumanCount = 1
-------------------------------------------------------------------------------
という結果が得られます
「う~ん全部なくなってるはずなのに0にならない」という問題はありますが
クラス変数、関数については理解できたと思います。
ちなみに、「HumanCountPrint」をprivate宣言に変えると、コンパイルエラーが出ます。
クラス関数を非公開にすると言う事は当クラスのインスタンス外からのアクセスを禁止する
と言う事になります。変数についても同様です。
という説明をするために、「HumanCountPrint」を公開関数にしたんですけどね。

最後はかっこよく0にしたいから、「HumanCountPrint」をコンストラクタ又はデストラクタで呼び出すように変更してみます。
--------------------------------------------------------------------------------

#include "iostream"
#include "cstring"
#include "cstdio"

class Human {
private:
static int HumanCount;
static void HumanCountAdd();
static void HumanCountSubtract();
static void HumanCountPrint(char*);

char *Name;
int Age;
char Birthday[9];
public:
Human(char*, int, char*);
~Human();

};

int Human::HumanCount = 0;

void Human::HumanCountAdd() {
HumanCount++;
}

void Human::HumanCountSubtract() {
HumanCount--;
}

void Human::HumanCountPrint(char* Title) {
std::cout << Title << ":HumanCount = " << HumanCount << "\n";
}

Human::Human(char *InName, int InAge, char *InBirthday) {
Name = new char(strlen(InName));
strcpy(Name, InName);
Age = InAge;
strcpy(Birthday, InBirthday);
// std::cout << "(" << Name << ") Human::Human()\n"; // ←今回は削除
Human::HumanCountAdd(); // ここにカウントアップを追加

char str[32];
memset(str, 0, 32);
std::sprintf(str, "%s create", Name);
Human::HumanCountPrint(str);
}

Human::~Human() {
// std::cout << "(" << Name << ") Human::~Human()\n"; // ←今回は削除
char str[32];
memset(str, 0, 32);
std::sprintf(str, "%s delete", Name);
delete Name;
Human::HumanCountSubtract(); // ここにカウントダウンを追加

Human::HumanCountPrint(str);
}

int main(void) {
Human obj1("semona", 0, "20040101");
Human *obj2 = new Human("yuuna", 1, "20030101");

delete obj2;
return 0;
}

-------------------------------------------------------------------------------
実行結果
-------------------------------------------------------------------------------
semona create:HumanCount = 1
yuuna create:HumanCount = 2
yuuna delete:HumanCount = 1
semona delete:HumanCount = 0
--------------------------------------------------------------------------------
と言う事になります。

0 件のコメント: