画面上の点を定義するときに,上述のようなPoint::set() を使って
としてもよいが,Pointのインスタンスが生成された時点で, まだ実際の画面上の点に対応していないというのが気持ち悪い. Pointのインスタンスが生成されると同時に画面上の点と 対応がついてくれた方が何かと好都合だ.C++では,インスタンスが 生成されるときに,その初期化をする関数を定義することができる. このような関数をコンストラクタと呼ぶ.コンストラクタは, クラスの名前と同じ名前を持つ.こんな感じだ.Point p; p.set(10,20);
コンストラクタはインスタンスの初期化だけを行うので,返値をもたない (voidですらない).したがって,返値の型の宣言はない.上記のように コンストラクタを宣言した場合,Pointのインスタンスの宣言は,class Point { private: .... public: Point(int x, int y, int col) { color = col; set(x,y); } .... };
のようにする.また,newを使ってPointのインスタンスを 生成するときは,Point p(10,20,1);
のように記述する. コンストラクタも関数の一種なので多重定義が可能であり,引数の デフォルト値なども使うことができる.Point *p = new Point(10,20,1);
Pointのインスタンスが消滅するときは,画面上の点にも消えてもらわないと
都合が悪い.C++では,このようなインスタンスが消滅するときの
処理も書くことができる.このような処理を記述する関数をデストラクタと呼ぶ.
デストラクタは,クラス名の頭に‾
がついた名前を持つ.
こんな感じだ.
ここで,0という色は背景色のつもりである.デストラクタの宣言には コンストラクタの場合と同じく返値の型はなく,またデストラクタの場合は 引数がない.class Point { private: .... public: ‾Point() { draw_point(coord.x, coord.y, 0); } .... };
Pointのインスタンスに対してデストラクタが呼ばれるのは, 次のような場合だ.
このように,コンストラクタとデストラクタを使うと, データの初期化や後始末が自動的にできる.ただ, ちょっと注意が必要なのは,他のクラスのメンバとして Pointなどのクラスを使う場合だ. 例えば
のようなクラスを作った場合,p1, p2のコンストラクタを TwoPoints()のどこかで呼ばなければならない (呼ばないとエラーになるはずだ). それには,こんな風に記述する.class TwoPoints { private: Point p1; Point p2; public: TwoPoints() {...} };
TwoPoints()のコンストラクタの本体が呼ばれる前に, その各メンバのコンストラクタを呼ぶわけだ.これは場合によっては 不便なこともあるが,言語仕様なので仕方がない.ちなみに,メンバが PointでなくPoint*の場合,newを使って こんな風にかける.class TwoPoints { private: Point p1; Point p2; public: TwoPoints() :p1(0,0,1), p2(10,10,1) {...} };
newを使ってメンバを確保したときは,デストラクタの中で それらをdeleteした方が良い.こんな感じだ.class TwoPoints { private: Point *p1; Point *p2; public: TwoPoints() { p1 = new Point(0,0,1); p2 = new Point(10,0,1); ... } };
class TwoPoints { private: Point *p1; Point *p2; public: TwoPoints() { p1 = new Point(0,0,1); p2 = new Point(10,0,1); ... } ‾TwoPoints() { delete p1; delete p2; } };
コンストラクタに関して,もう一つ注意がある.Point の配列を作りたい場合,
このようなことをするためには,クラスPointで引数のない コンストラクタが定義されていなければならない.つまりPoint p[100];
というコンストラクタがないとだめだ,というわけである.クラスの 配列を取ったとき,その配列の各要素について,引数のないコンストラクタ が呼ばれる.class Point { ... public: Point() { ... } };