第4回目は
クラスのコンストラクタ、デストラクタという機能です。
コンストラクタというのは、
クラスの変数が宣言されたとき、必ずよばれる関数のことです。
デストラクタはクラスの変数の寿命が来て、
変数が破棄されるときに必ず呼ばれる関数です。
変数の寿命が尽きるのは
たとえば、ある関数内で変数を宣言をして、その関数を抜けるときです
変数宣言時から{} で囲まれた(ブロックという)所を抜けるときが変数の寿命です
グローバル変数の場合はプログラムが終了時に寿命が尽きます。
コンストラクタを定義するには、
クラス名と同名の戻り値がない関数を書きます(void型ではない)
デストラクタを定義するには
コンストラクタ同様、クラス名と同名の関数に
「~」(←チルダという記号)を先頭につけます。
たとえば、Myclassというクラスの
コンストラクタはMyclass()という関数になり
デストラクタは~Myclass()という関数になります
#include <iostream> using namespace std; class Myclass//←クラス名Myclass { private: public: // コンストラクタ(戻り値かかない+クラス名の関数) Myclass() { cout << "変数宣言時に必ずよばれます" << endl; } // デストラクタ(戻り値かかない+クラス名の関数先頭に「~」) ~Myclass() { cout << "変数の寿命が尽きるとき、必ず呼ばれる" << endl; } }; int main() { Myclass mc;//←Myclass型の変数宣言時に必ずコンストラクタが呼ばれる return 0; }//←変数の寿命が尽きるとき、必ずMyclassのデストラクタが呼ばれる
注意としては、コンストラクタやデストラクタをpublicで宣言しないと
変数の宣言時に外部(ここではmain関数)から呼び出せなくなってエラーになります
また、コンストラクタやデストラクタも関数のオーバロードをすることができます。
(デストラクタについてはオーバロードさせる事例があまりありませんが・・・)
コンストラクタは基本的にクラス内部の変数を初期化することに用います
デストラクタはクラス内の動的メモリが確保された変数のメモリを解放するときに用います
(C++の変数の動的な確保の方法については次回説明)
コンストラクタやデストラクタは変数ごとに別々に呼び出されます
お互いが独立したものと考えてください
次の例はコンストラクタをオーバーロードして入力ができるようにし、
クラスの変数を2種類の方法で宣言して、それぞれのコンストラクタを呼び出しています
#define _CRT_SECURE_NO_WARNINGS #include <string.h> #include <iostream> using namespace std; class Myclass { private: int a; char name[256]; public: // コンストラクタ Myclass() { a = 10; strcpy(name,"default"); cout << "引数なしコンストラクタが呼ばれました" << endl; cout << "name = " << name << " a = " << a << endl; } // コンストラクタのオーバーロード Myclass(int input,char *inputname):a(input) { strcpy(name,inputname); cout << "引数つきコンストラクタが呼ばれました" << endl; cout << "name = " << name << " a = " << a << endl; } // デストラクタ ~Myclass() { cout << "name = " << name << "は消されました " << endl; } }; int main() { Myclass mc1; // ←引数なしコンストラクタ Myclass mc2(10,"mc2"); // ←引数つきコンストラクタの呼び出し return 0; }//←変数の寿命が尽きるとき、必ずMyclassのデストラクタが呼ばれる
mc1は引数なしのコンストラクタが呼ばれ、
mc2は引数つきのコンストラクタが呼ばれます
ここで、オーバーロードしているコンストラクタのほうで
:a(input)と書いていることに気づいていると思います
これは初期化子と呼ばれ
a = input; の文を書いていることと同じです
Myclass(int input,char *inputname) { a = input; strcpy(name,"default"); }
としても同じですが、初期化子での代入のほうが
処理速度が通常の代入より速いです
結構細かいことになるので詳細はここには書きませんが、
初期化子を使って初期化することも覚えておいてください
追記としては、配列のコンストラクタ初期化子は存在しません
クラス内でmemcpy,strcpyなどするしかないです
初期化子を連続で書く場合は
間に「,」をはさんで連続で書きます
class Myclass { private: int a,b; public: Myclass(int inputA,int inputB);a(inputA),b(inputB) {} };
なお連続で変数を書くときは
Myclass mc1,mc2(10,"mc2");
になります。
デストラクタについて細かいことですがデストラクタの呼び出し順番は
変数の宣言順と逆順となっていることに注意してください
この理由は宣言と逆順で後処理をしないと
後処理がおかしなことになることが多いからこうゆう仕様になっています。
(めんどくさい話になるのでここでは具体例を挙げませんが・・・)