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