クラスの宣言と初期化
前回以下のようなクラスを作りました。
class cGameChar //クラス 基本的にプライベート { private: //アクセス権 int hp; int mp; public: //アクセス権 //setter セッター関数 void sethp(int _hp); //関数宣言 void setmp(int _mp); //getter ゲッター関数 int gethp(){ return(this->hp); } //インライン定義 int getmp(){ return(this->mp); } //インライン定義 };
メンバは、
メンバ変数が、
- int hp
- int mp
メンバ関数が
- void sethp(int _hp)
- void setmp(int _mp)
- int gethp()
- int getmp()
を持っている。
メンバ変数はアクセス指定が「private」に設定されているので、インスタンス(クラス変数)からは直接アクセスできません。
すなわち
cGameChar player, enemy; //クラスのインスタンスを生成 player.hp = 50; player.mp = 30; // ↑ プライベートなメンバはインスタンスから直接書き換えできない。 player.sethp(50); player.setmp(30); //これはセッターゲッター関数からのアクセスなのでOK
のように、セッターや、ゲッターなどのアクセス関数からアクセスするのが掟である。
インスタンスの初期化
授業開始からずーーーーっと言ってきたことに変数の初期化がある。
変数は、その値が参照される前に必ず初期化されなければならない。という事である。
int a; //int a=5;初期化と宣言を同時にもできるよ int b; a=5; //初期化というよりは1回目の代入 cout << a << endl; //初期化されているので、設定された値を表示する cout << b << endl; //初期化されていないので、何がどうなるか不明
このことは、クラスや構造体も同様であって、初期化していないクラスや構造体のインスタンスは参照できません。
cGameChar player, enemy; //クラスのインスタンスを生成 player.gethp();//メンバ関数がゴソっと見初期化なのでエラーになる。
コンストラクタによる初期化
classにはこのような見初期化問題を防ぐために、メンバ関数を初期化するための機構が備わっている。
それが、コンストラクタである。
コンストラクタの形
コンストラクタは、以下のように決まった形をしています。
class foo { int x; public: foo(); //引数なしコンストラクタ foo(int _x); //引数ありコンストラクタ }; //引数なしコンストラクタの定義 foo:foo(){ x = 0; //xを0で初期化 } //引数ありコンストラクタの定義 foo:foo(int _x){ x = _x; //xを_xで初期化 }
練習問題
cGameCharクラスのコンストラクタを書いてみよう!
#include <iostream> using namespace std; //クラス宣言 (クラスのメンバを書き連ねる) class cGameChar //クラス 基本的にプライベート { private: //アクセス権 int hp; int mp; public: //アクセス権 //コンストラクタ cGameChar(); //引数無しのコンストラクタ cGameChar(int _hp, int _mp); //setter セッター関数 void sethp(int _hp); //関数宣言 void setmp(int _mp); //getter ゲッター関数 int gethp(){ return(this->hp); } //インライン定義 int getmp(){ return(this->mp); } //インライン定義 void printCharStatus(); //HP,MPを表示する関数 }; //コンストラクタの定義 cGameChar::cGameChar() { hp = 0; //メンバの初期化 mp = 0; //メンバの初期化 cout << "コンストラクタが呼ばれ、メンバが初期化されました" << endl; } void cGameChar::printCharStatus() { cout << "HP: " << hp << endl; cout << "MP: " << mp << endl; } //関数定義 void cGameChar::sethp(int _hp) { this->hp = _hp; } void cGameChar::setmp(int _mp) { this->mp = _mp; } // cGameCharの中では、自分のものは全部使うことができる! //class宣言は設計図 int main() { cGameChar hero,boss; //←引数無しコンストラクタが呼ばれるよ cGameChar hero(500,300),boss(1000,100); //←引数付きコンストラクタを書いてみよう(5時間目にやろうか) hero.printCharStatus(); }
初期化をメンバイニシャライザに任せてみる
今まで、コンストラクタの中でメンバ変数に値を代入することでメンバの初期化を行ってきました。
(別にそれでもいいです。ただしメンバ変数は、(一部の特殊なものを除いて)コンストラクタの中で必ず初期化をするべきです。
これを、メンバイニシャライザを使って書き換えてみます。
- "今までの手法"
cGameChar::cGameChar() { this->name = ""; this->hp = 0; //メンバの初期化 this->mp = 0; //メンバの初期化 cout << "コンストラクタが呼ばれ、メンバが初期化されました" << endl; }
これを、以下のように書けます
- "メンバイニシャライザを使った初期化"
cGameChar::cGameChar() :name(""),hp(0),mp(0) //←これがメンバイニシャライザ { //this->name = ""; //this->hp = 0; //メンバの初期化 //this->mp = 0; //メンバの初期化 cout << "コンストラクタが呼ばれ、メンバが初期化されました" << endl; }
引数付きのコンストラクタも同様に書けます。(以下の全体のソースコード、参照)
全体のソースコード
- "main.cpp"
#include <iostream> #include <string> using namespace std; //クラス宣言 (クラスのメンバを書き連ねる) class cGameChar //クラス 基本的にプライベート { private: //アクセス権 string name; int hp; int mp; public: //アクセス権 //コンストラクタ cGameChar(); //引数無しのコンストラクタ cGameChar(string _name, int _hp, int _mp);//引数付きのコンストラクタ //setter セッター関数 void sethp(int _hp); //関数宣言 void setmp(int _mp); //getter ゲッター関数 int gethp(){ return(this->hp); } //インライン定義 int getmp(){ return(this->mp); } //インライン定義 void printCharStatus(); //HP,MPを表示する関数 }; //コンストラクタの定義 cGameChar::cGameChar() :name(""),hp(0),mp(0) { //this->name = ""; //this->hp = 0; //メンバの初期化 //this->mp = 0; //メンバの初期化 cout << "コンストラクタが呼ばれ、メンバが初期化されました" << endl; } //引数付きのコンストラクタの定義(オーバーロード) cGameChar::cGameChar(string _name, int _hp, int _mp) :name(_name), hp(_hp), mp(_mp)// ← メンバイニシャライザ { cout << "引数付きコンストラクタが呼ばれました。" << endl; } void cGameChar::printCharStatus() { cout << "=+=+=+=+=+=+=+=+=+=+" << endl; cout << "NAME: " << name << endl; cout << " HP: " << hp << endl; cout << " MP: " << mp << endl; cout << "=+=+=+=+=+=+=+=+=+=+" << endl; } //関数定義 void cGameChar::sethp(int _hp) { this->hp = _hp; } void cGameChar::setmp(int _mp) { this->mp = _mp; } // cGameCharの中では、自分のものは全部使うことができる! //class宣言は設計図 int main() { //cGameChar hero, boss; cGameChar hero("おるてが", 500,300); cGameChar boss("どるまげす", 1000,100); hero.printCharStatus(); boss.printCharStatus(); }
コンストラクタの呼ばれるタイミング
コンストラクタは、オブジェクト(インスタンス)が構築されるとき(メモリが確保されるとき)に呼ばれます。
実行して、いつ呼ばれているか確認してみてください。
- "コンストラクタの呼ばれるタイミング"
int main() { //cGameChar hero, boss; cGameChar hero("おるてが", 500,300); cGameChar boss("どるまげす", 1000,100); hero.printCharStatus(); boss.printCharStatus(); cGameChar *pChar = new cGameChar("フリオニール", 320, 999); }