新規記事の投稿を行うことで、非表示にすることが可能です。
2017年12月11日
《その173》 コピーコンストラクタ,デストラクタ,代入演算子 の継承
コピーコンストラクタ,デストラクタ,代入演算子 の継承
基底クラスからの派生によって作成される派生クラスについて、デフォルトコンストラクタ,コピーコンストラクタ,代入演算子が、どのような形で継承されるのかを、下のプログラムで確認してみます。
◆ クラス Array を基底クラスとして、public派生により 派生クラス ArrayX を作ります。
最初に、下のプログラムの説明を書きます。
【基底クラス Array について】
配列要素数 num = 5
p は配列の先頭要素へのポインタ用の変数です。
char型の chr には、main関数内で作成する クラスオブジェクト a, b, c を識別するために、それぞれ文字 'a', 'b', 'c' を、実行時に代入します。プログラム終了時にクラスオブジェクトが、デストラクタによって破壊される直前に、この文字を表示します。
Array() は引数無しのデフォルトコンストラクタです。
new int[num] により int型配列用の領域を動的に確保して、その先頭要素のアドレスをポインタ p に代入します。
Array(const Array& x) はコピーコンストラクタです。
コピー元の配列 int[x.num] と同じ大きさの領域を動的に確保し、そこに、コピー元の配列要素をすべてコピーします。
~Array() はデストラクタです。
プログラム終了時に、クラスオブジェクトが破壊されるときに呼ばれます。動的に確保した記憶領域を解放します。また、 'a', 'b', 'c' のいずれかの文字を表示して自分の正体をアピールします。
Array& operator=(const Array& x) は代入演算子の多重定義です。
代入演算子「=」が、Array型オブジェクトを扱えるようにするための多重定義です。
代入される側のオブジェクトが、自分自身の中に、コピー元の配列 int[x.num] と同じ大きさの領域を動的に確保し、そこに、コピー元の配列要素をすべてコピーします。
メンバ関数 void set(int v)
配列 p の全要素に値 v を代入します。
メンバ関数 void print() const
配列 p の全要素の値を表示します。
【派生クラス ArrayX について】
クラス Array を親として、public派生により 作成される子クラスです。継承の際は、コンストラクタ,デストラクタ,代入演算子 を定義せず、すべて基底クラス Array の資産を利用するものとします。
int k は、派生クラス ArrayX独自のデータメンバです。
void set_k(int x) は、派生クラス ArrayX独自のメンバ関数で、k のセッタです。
// ------------------------------------
#include <iostream>
using namespace std;
class Array { /* ◆基底クラス */
const int num = 5;
int *p;
public:
char chr;
Array() : p(new int[num]) {
cout << "デフォルトコンストラクタ(領域確保)\n";
}
// ↑デフォルトコンストラクタ
// 要素数 num の int型配列用領域を確保
Array(const Array& x) : p(new int[x.num]) {
for (int i = 0; i < num; i++) p[i] = x.p[i];
cout << "コピーコンストラクタ\n";
}
// ↑コピーコンストラクタ
~Array() {
delete[] p;
cout << "デストラクタ, chr … " << chr << '\n';
}
// ↑デストラクタ
Array& operator=(const Array& x) {
cout << "代入演算子\n";
for (int i = 0; i < num; i++)
p[i] = x.p[i];
return *this;
}
// ↑代入演算子
void set(int v) {
for (int i = 0; i < num; i++) p[i] = v;
}
// ↑メンバ関数
// 全要素に値 v を代入
void print() const {
for (int i = 0; i < num; i++) cout << p[i] << ' ';
}
// ↑メンバ関数
// 全要素の値を表示
};
class ArrayX : public Array { /* ◆派生クラス */
int k;
public:
int get_k() const { return k; }
void set_k(int x) { k = x; }
};
// コンストラクタ,コピーコンストラクタ,デストラクタ
// の定義はしていない。
int main() {
ArrayX a; // ArrayX a を作成
/* ↑デフォルトコンストラクタが作動 */
a.set(10); // aの全要素に 10 を代入
a.set_k(99); // a.k に 99 を代入
ArrayX b(a); // ArrayX b を作成(aで初期化)
/* ↑コピーコンストラクタが作動 */
ArrayX c; // ArrayX c を作成
/* ↑デフォルトコンストラクタが作動 */
c = a;
/* ↑代入演算子が作動(aの全要素をcにコピー) */
a.chr = 'a'; b.chr = 'b'; c.chr = 'c';
cout << '\n';
cout << "配列a : "; a.print(); cout << '\n';
cout << " k : " << a.get_k() << '\n';
cout << "配列b : "; b.print(); cout << '\n';
cout << " k : " << b.get_k() << '\n';
cout << "配列c : "; c.print(); cout << '\n';
cout << " k : " << c.get_k() << '\n';
cout << '\n';
}
// ------------------------------------
プログラムの実行結果を見ると・・・、
派生クラス ArrayX の中では、コピーコンストラクタ,デストラクタ,代入演算子 が定義されていないのですが、基底クラス Array の コピーコンストラクタ,デストラクタ,代入演算子 が、派生クラスに対しても、きちんと動作しているようです。
また、派生クラス独自のデータメンバである k や、ゲッタ get_k(),セッタ set_k() も、期待通りにコピーされています。
◆ 派生クラス内で、コピーコンストラクタ,デストラクタ,代入演算子 が定義されなければ、基底クラスのものと実質的に同じ働きをするものが、コンパイラにより自動的に定義されます。
◆ 派生クラス ArrayX内で自動的に定義される代入演算子は、次の形式のものです。
ArrayX& ArrayX::operator=(const ArrayX&);
◆ 自動的に定義される コピーコンストラクタ,デストラクタ,代入演算子 は、inline かつ public です。