新規記事の投稿を行うことで、非表示にすることが可能です。
2017年12月17日
《その184》 仮想関数(2)
仮想関数
前回《183》のプログラムと今回のプログラムの相違点は、
基底クラス Member のメンバ関数の関数宣言 void print() const; の前に virtual を付加して、
virtual void print() const;
としてある箇所( ◆印 )だけです。
これで、関数 print は仮想関数になります。
派生クラスのメンバ関数で、基底クラスの仮想関数と仕様(関数名と仮引数)が同じものは、virtual が付加されていなくても仮想関数とみなされます。
したがって、Member2クラスのメンバ関数 print も仮想関数です。
プログラムコードより実行結果が先になってしまいますが、実行結果は次のようになります。
上が前回《183》の実行結果、下が今回(メンバ関数 print が仮想関数)の実行結果です。
【 前回《183》の実行結果 】
【 今回の実行結果 】
今回のプログラムは、関数 print が仮想関数になっている点が、前回と違っています。
仮想関数は、x.print() として呼ばれたときに、x の静的な型ではなく、動的な型のメンバ関数を呼び出します。
動的な型とは、ポインタが指しているオブジェクトの実際の型であり、
harada, tahara であれば Member型、
kawada, tagawa であれば Member2型
ということになります。
その結果、x.print により、
harada, tahara では、Member::print()、
kawada, tagawa では、Member2::print()、
が実行されることになります。
以下が今回のプログラムです。
// ------------------------------------
#include <string>
#include <iostream>
using namespace std;
class Member {
public:
int number; // 番号
double best_time; // 自己ベスト
Member(int n, double t)
: number(n), best_time(t) { }
// 個人データの出力
virtual void print() const { // ◆
// void print() const {
cout << "【" << number << "】"
"自己ベスト : "
<< best_time << '\n';
}
};
class Member2 : public Member {
public:
char mf; // 性別
Member2(int n, double t, char c)
: Member(n, t), mf(c) { }
// 個人データの出力
void print() const {
cout << "【" << number << "】"
"性別 (" << mf << ") "
"自己ベスト : "
<< best_time << '\n';
}
};
int main() {
// Member型オブジェクトの生成
Member harada(1001, 13.2);
Member tahara(2001, 12.2);
// Member2型オブジェクトの生成
Member2 kawada(3002, 11.8, 'm');
Member2 tagawa(3006, 12.2, 'f');
Member* ptr[] = { &harada, &tahara, &kawada, &tagawa };
for (int i = 0; i < sizeof(ptr) / sizeof(ptr[0]); i++)
(*ptr[i]).print();
}
// ------------------------------------
《その183》 仮想関数(1)
仮想関数
下のプログラムは、仮想関数の働きを見るためのものです。
プログラム中の◆◆◆印の部分が仮想関数ですが、このプログラムでは、その部分をコメントアウトしてあります。
次回《184》にコメントアウトを外して、その働きを見てみることになります。
プログラム中に、2種類のクラスがあります。違いは次の2点です。
(1) クラス Member
・データメンバ : 番号 ・自己ベスト
・関数 print は、
番号, 自己ベスト を出力
(2) public派生クラス Member2
・データメンバ : 番号 ・自己ベスト ・性別
・関数 print は、
番号, 性別, 自己ベスト を出力
各オブジェクトを、
Member* ptr[] = { &harada, &tahara, &kawada, &tagawa };
のように、基底クラス型のポインタ ptr[0] 〜 ptr[3] で、指している点に注意してください。
末尾に載せてある出力結果を見ると、番号, 自己ベストだけが出力されています。
このことから、4人分のオブジェクトが全て Member型として扱われたことが分かります。
// ------------------------------------
#include <string>
#include <iostream>
using namespace std;
class Member {
public:
int number; // 番号
double best_time; // 自己ベスト
Member(int n, double t)
: number(n), best_time(t) { }
// 個人データの出力
// virtual void print() const { // ◆◆◆
void print() const {
cout << "【" << number << "】"
"自己ベスト : "
<< best_time << '\n';
}
};
class Member2 : public Member {
public:
char mf; // 性別
Member2(int n, double t, char c)
: Member(n, t), mf(c) { }
// 個人データの出力
void print() const {
cout << "【" << number << "】"
"性別 (" << mf << ") "
"自己ベスト : "
<< best_time << '\n';
}
};
int main() {
// Member型オブジェクトの生成
Member harada(1001, 13.2);
Member tahara(2001, 12.2);
// Member2型オブジェクトの生成
Member2 kawada(3002, 11.8, 'm');
Member2 tagawa(3006, 12.2, 'f');
Member* ptr[] = { &harada, &tahara, &kawada, &tagawa };
for (int i = 0; i < sizeof(ptr) / sizeof(ptr[0]); i++)
(*ptr[i]).print();
}
// ------------------------------------
《その182》 静的な型
静的な型
次のプログラムで、★印のポインタ p_b,参照 r_b の指す先は、両方ともクラスオブジェクト bbb ですが、p_b,r_b を typeid演算子で評価して得られるのは aaa型です。
このように、プログラム実行時の意味的には別の型であっても、宣言時などから判断できる本来の型を、「静的な型」と表現します。
// ------------------------------------
#include <typeinfo>
#include <iostream>
class Aaa {
public:
int a;
Aaa() : a(1) { }
};
class Bbb : public Aaa {
public:
int b;
Bbb() : b(2) { }
};
int main() {
Aaa aaa;
Bbb bbb;
std::cout << "(1)\n";
std::cout << typeid(Aaa).name() << '\n';
std::cout << typeid(Bbb).name() << "\n\n";
Aaa* p_a = &aaa;
Aaa* p_b = &bbb; // ★
// 基底クラスへのポインタは
// 派生クラスを指すことができる。
std::cout << "(2-1)\n";
std::cout << typeid(*p_a).name() << '\n';
std::cout << typeid(*p_b).name() << "\n\n";
std::cout << "(2-2)\n";
std::cout << typeid(p_a).name() << '\n';
std::cout << typeid(p_b).name() << "\n\n";
// ポインタ p_b は派生クラスを指しますが、
// そのポインタ型は基底クラス型
// です。
Aaa& r_a = aaa;
Aaa& r_b = bbb; // ★
// 基底クラス型の参照は、派生クラスを参照
// することができます。
std::cout << "(3-1)\n";
std::cout << typeid(r_a).name() << '\n';
std::cout << typeid(r_b).name() << "\n\n";
// r_b は bbb を参照しますが、その型
// は基底クラス型です。
std::cout << "(3-2)\n";
std::cout << typeid(&r_a).name() << '\n';
std::cout << typeid(&r_b).name() << "\n\n";
// 以下は自分の復習用なので、今回のテーマとは
// 関係ありません。
int n = 99;
int* p = &n;
int& r = n;
std::cout << *p << '\n';
std::cout << r << "\n\n";
*p = 100;
std::cout << *p << '\n';
std::cout << r << "\n\n";
r = 999;
std::cout << *p << '\n';
std::cout << r << "\n\n";
std::cout << typeid(*p).name() << '\n';
std::cout << typeid(r).name() << "\n\n";
std::cout << typeid(p).name() << '\n';
std::cout << typeid(&r).name() << '\n';
}
// ------------------------------------
2017年12月16日
《その181》 メンバ関数の隠蔽(派生クラス内で同名の関数を定義)
基底クラスのメンバ関数の隠蔽
派生クラス内で、基底クラスのメンバ関数と同名の関数を定義すると、基底クラスのメンバ関数は隠蔽されます。
次のプログラムでは、基底クラス Aaa がメンバ関数 print を持っていて、派生クラス Bbb も同名のメンバ関数 print を持っています。このようなときは、基底クラスのほうのメンバ関数が隠蔽されます。
Aaa のメンバ関数は print();
Bbb のメンバ関数は print(int x);
で、仮引数が異なりますが、仮引数とは関係なく関数名が同じであれば隠蔽されます。
基底クラス Aaa のメンバ関数を呼び出す場合には、有効範囲解決演算子「 :: 」を用いて Aaa::print() のようにします。
// ------------------------------------
#include <string>
#include <iostream>
class Aaa {
int a;
public:
Aaa(int x = 10) : a(x) { }
void print() { std::cout << "a = " << a << '\n'; }
// クラス Aaa のメンバ関数
};
class Bbb : public Aaa{
int b;
public:
Bbb(int x = 20, int y = 200) : Aaa(x), b(y) { }
void print(int x) {
std::cout << "整数 " << x
<< " を受け取りました。\n";
std::cout << "b = " << b << '\n'; }
// クラス Bbb のメンバ関数
// 基底クラス Aaa のメンバ関数と同名なので、
// 基底クラスの関数 print は隠蔽されます。
};
int main() {
Aaa aaa; // Aaa型オブジェクトの aaa を生成。
Bbb bbb; // Bbb型オブジェクトの bbb を生成。
aaa.print();
std::cout << '\n';
bbb.print(66);
// Bbb型の関数 print が呼び出されます。
// (Aaa型の関数 print は隠蔽されているため)
std::cout << '\n';
bbb.Aaa::print();
// 有効範囲解決演算子「 :: 」を用いて Aaa::print
// とすれば、基底クラス Aaa の関数 print
// にアクセスすることができます。
}
// ------------------------------------
《その180》 継承と静的メンバ & p.174演習4-4
継承と静的メンバ
静的メンバを含むクラスから派生した子クラスは、親クラスである基底クラスの静的メンバを、そのまま静的メンバとして継承します。
次のプログラムで、その様子が確認できます。
親クラス型のオブジェクト,子クラス型のオブジェクト,孫クラス型のオブジェクトが、それぞれのオブジェクト型とは無関係に、作成された順にマイナンバー my_num を割り当てられています。
// ------------------------------------
#include <iostream>
class Num {
static int num;
// Num型のオブジェクトを作成した個数を記録する
// ための静的データメンバです。
int my_num;
// 各オブジェクト自身が、何番目に作られたもので
// あるか、オブジェクト作成時にその
// 値で初期化されます。
public:
Num() : my_num(++num) { }
// コンストラクタ
// オブジェクトを作成する毎に、num の値をイ
// ンクリメントし、
// その値で、各オブジェクトの my_num を初期
// 化します。
void my_n() const {
std::cout << my_num << '\n';
}
// my_num のゲッタです。
};
/* public派生した子クラス */
class Aaa : public Num {
int a;
public:
Aaa(int x = 1000) : a(x) { }
int get_a() { return a; }
};
/* public派生した孫クラス */
class Bbb : public Aaa {
int b;
public:
Bbb(int x = 2000) : b(x) { }
int get_b() { return b; }
};
int Num::num = 0;
int main()
{
Num n1; // 1個目の Num型オブジェクト
n1.my_n(); // 1
Num n2; // 2個目の Num型オブジェクト
n2.my_n(); // 2
Aaa a1; // 1個目の Aaa型オブジェクト
a1.my_n(); // 3
Aaa a2; // 2個目の Aaa型オブジェクト
a2.my_n(); // 4
Bbb b1; // 1個目の Bbb型オブジェクト
b1.my_n(); // 5
Num n3; // 3個目の Num型オブジェクト
n3.my_n(); // 6
}
// ------------------------------------
新版明解C++中級編 p.174 演習4-4
次のクラス ResigningMember へのポインタからクラス Member へのポインタへのアップキャストが行えるかどうか、プログラムを作成して確認せよ。
// ------------------------------------
/* 一般会員クラス Member */
class Member {
std::string full_name; // 氏名
int number; // 会員番号
double weight; // 体重
public:
Member(const std::string& name, int no, double w)
: full_name(name), number(no) {
set_weight(w); // 体重を設定
}
// full_nameのゲッタ
std::string name() const {
return full_name;
}
// numberのゲッタ
int no() const { return number; }
// weightのゲッタ
double get_weight() const {
return weight;
}
// weightのセッタ
void set_weight(double w) {
weight = (w > 0) ? w : 0;
}
};
/* 退会済み会員クラス ResigningMember */
class ResigningMember : private Member {
public:
ResigningMember(
const std::string& name,
int number,
double w
) : Member(name, number, w) { }
// weightのゲッタを再定義
double get_weight() {
std::cout
<< "退会した会員の体重の"
"取得はできません。\n";
return 0;
}
using Member::no; // using宣言
};
// ------------------------------------
◆以下が解答のプログラムです。
// p174_演習4-4
#include <string>
#include <iostream>
/* 一般会員クラス MeMber */
class Member {
std::string full_name; // 氏名
int number; // 会員番号
double weight; // 体重
public:
Member(const std::string& name, int no, double w)
: full_name(name), number(no) {
set_weight(w); // 体重を設定
}
// full_nameのゲッタ
std::string name() const {
return full_name;
}
// numberのゲッタ
int no() const { return number; }
// weightのゲッタ
double get_weight() const {
return weight;
}
// weightのセッタ
void set_weight(double w) {
weight = (w > 0) ? w : 0;
}
};
/* 退会済み会員クラス ResigningMember */
class ResigningMember : private Member {
public:
ResigningMember(
const std::string& name,
int number,
double w
) : Member(name, number, w) { }
// weightのゲッタを再定義
double get_weight() { // ★2.
std::cout
<< "退会した会員の体重の"
"取得はできません。\n";
return 0;
}
using Member::no; // using宣言★1.
};
int main()
{
ResigningMember oosugi("大杉毬藻", 26, 50.7);
//■// Member* ptr1 = &oosugi; // ← エラー
// [コンパイラからのメッセージ]
// (1) アクセスできない基底クラス "Member"
// への変換は許可されていません。
// (2) '型キャスト': 'ResigningMember *'
// から'Member *'の変換は存在し
// ますが、アクセスできません。
ResigningMember* ptr2 = &oosugi; //▲//
std::cout << "会員番号 : "
<< ptr2->no() << '\n';
// ★1.印のusing宣言により、アクセス権あり。
// cout << "氏名 : " << oosugi.name()
// << '\n'; // ← エラー
std::cout << "体重 : "
<< ptr2->get_weight() << '\n';
// ★2.印の再定義により、元々の get_weight
// は隠蔽され、再定義され
// た get_weight が呼ばれ
// る。
// oosugi.set_weight(45.3); // ← エラー
/*
上の■印の結果によれば、クラス ResigningMember
へのポインタからクラス Member へのポインタへの
アップキャストは、行えない、と言えます。
*/
// ************ 以上が解答です ************
// ************ 以下は付け足し ************
/*
上のプログラムの、■印のようなやり方で、直接的
に、Resigning型オブジェクトである oosugi へのポイ
ンタを、Member*型の変数 ptr1 に代入しようとする
アップキャストは、エラーになります。
そこで、一旦、上のプログラムの▲印にもあるような、
ResigningMember* ptr2 = &oosugi;
という、問題のない方法で ResigningMember*型の
ポインタ ptr2 を作成した上で、そのポインタを次のよう
にアップキャストしてみます。
Member* ptr3 = (Member*)ptr2;
このようにして作成したポインタ ptr3 を使用する
と、次のようなアクセスが可能になります。
すなわち、
派生クラスオブジェクト oosugi の中に
基底クラス部分オブジェクトとして存在する基底クラスオブ
ジェクトMember のメンバ関数 get_weight() に、アク
セスできるようになります。
つまり、
std::cout << ptr3->get_weight() << '\n';
が、可能になります。
*/
std::cout << "\n以下は、付け足しです。\n";
Member* ptr3 = (Member*)ptr2;
std::cout << ptr3->get_weight() << '\n';
}
2017年12月15日
《その179》 using宣言によるアクセス権の調整 & p.174演習4-3
using宣言によるアクセス権の調整
次のプログラムで、基底クラス Base は、
private:
int型の a
public:
コンストラクタ Base
関数 func1
で構成されています。
また、基底クラス Base から private派生によって作られる派生クラス Derived は、実装された基底クラスのメンバに加えて、
private:
int型の b
public:
コンストラクタ Derived
関数 func2
で構成されています。
private派生であるため、基底クラス Base では公開メンバであった 関数 func1 は、派生クラス Derived においては、基本的には外部から呼び出すことができません。
この func1 のアクセス権を using宣言により調整することができます。アクセス権を調整することによって、派生クラス Derived の外部から、関数 func1 を呼び出せるようにできるのです。
下の ★1. の箇所が using宣言です。★2. の箇所で、アクセス権を調整して使えるようになった func1 を利用しています。
// ------------------------------------
#include <iostream>
class Base {
int a;
public:
Base(int aa) : a(aa) { }
void func1() const {
std::cout << "a = " << a << '\n';
}
};
class Derived : private Base {
int b;
public:
Derived(int aa, int bb) : Base(aa), b(bb) { }
void func2() const {
std::cout << "b = " << b << '\n';
}
using Base::func1; // ★1.
};
int main()
{
Derived drvd(11, 22);
drvd.func1(); // ★2.
drvd.func2();
}
// ------------------------------------
新版明解C++中級編 p.174 演習4-3
次のクラス Base型と Derived型に対して、アップキャストとダウンキャストを試みるプログラムを作成せよ。
// ------------------------------------
class Base {
int a;
int b;
public:
Base(int aa, int bb) : a(aa), b(bb) { }
void func() const {
std::cout << "a = " << a << '\n';
std::cout << "b = " << b << '\n';
}
};
class Derived : public Base {
int x;
public:
Derived(int aa, int bb, int xx) : Base(aa, bb), x(xx) { }
void method() const {
func();
std::cout << "x = " << x << '\n';
}
};
// ------------------------------------
◆以下が解答のプログラムです。
// p174_演習4-3
#include <iostream>
class Base {
int a;
int b;
public:
Base(int aa, int bb) : a(aa), b(bb) { }
void func() const {
std::cout << "a = " << a << '\n';
std::cout << "b = " << b << '\n';
}
};
class Derived : public Base {
int x;
public:
Derived(int aa, int bb, int xx) : Base(aa, bb), x(xx) { }
void method() const {
func();
std::cout << "x = " << x << '\n';
}
};
int main()
{
Base base(10, 20);
Derived drvd(11, 22, 33);
// ◆ ------------------▽
Base* ptr_b1 = &base;
ptr_b1->func(); // a = 10
// b = 20
// ptr_b1->method(); /* エラー */
// ---------------------△
// ◆ アップキャスト----▽
Base* ptr_b2 = &drvd;
ptr_b2->func(); // a = 11
// b = 22
// ptr_b2->method(); /* エラー */
// ---------------------△
// ◆ ダウンキャスト----▽
// Derived* ptr_d1 = &base; /* エラー */
// ---------------------△
// ◆ ------------------▽
Derived* ptr_d2 = &drvd;
ptr_d2->func(); // a = 11
// b = 22
ptr_d2->method(); // a = 11
// b = 22
// x = 33
}
// ---------------------△
《その178》 クラスのメンバーへのアクセス
クラスのメンバーが、例えば、次の a, b, c である場合、
private:
int a;
protected:
int b;
public:
int c;
a には、定義されたクラスの メンバー関数,フレンド関数からアクセスできます。
b には、定義されたクラスやその派生クラスの メンバー関数,フレンド関数からアクセスできます。
c には、どこからでもアクセスできます。
さらに、このクラスからの private派生,protected派生,public派生 による派生クラスの場合には、a, b, c へのアクセス可否がどのようになるのか、一応、確認してみました。
// ------------------------------------
using namespace std;
/* 基底クラス(親クラス) */
class Class00 {
private:
int a;
protected:
int b;
public:
int c;
Class00() : a(10), b(20), c(30) { }
void get_a() { cout << a << '\n'; }
void get_b() { cout << b << '\n'; }
void get_c() { cout << c << '\n'; }
};
// ------------------
/* private派生(子クラス) */
class Class11 : private Class00 {
public:
// void get_a() { cout << a << '\n'; } // エラー
void get_b() { cout << b << '\n'; }
void get_c() { cout << c << '\n'; }
};
/* private派生(孫クラス) */
class Class12 : private Class11 {
public:
// void get_a() { cout << a << '\n'; } // エラー
// void get_b() { cout << b << '\n'; } // エラー
// void get_c() { cout << c << '\n'; } // エラー
};
// ------------------
/* protected派生(子クラス) */
class Class21 : protected Class00 {
public:
// void get_a() { cout << a << '\n'; } // エラー
void get_b() { cout << b << '\n'; }
void get_c() { cout << c << '\n'; }
};
/* protected派生(孫クラス) */
class Class22 : protected Class21 {
public:
// void get_a() { cout << a << '\n'; } // エラー
void get_b() { cout << b << '\n'; }
void get_c() { cout << c << '\n'; }
};
// ------------------
/* public派生(子クラス) */
class Class31 : public Class00 {
public:
// void get_a() { cout << a << '\n'; } // エラー
void get_b() { cout << b << '\n'; }
void get_c() { cout << c << '\n'; }
// ゲッタ get_a(), get_b(), get_c()は public の
// ままで継承されているので記述不要。
};
/* public派生(孫クラス) */
class Class32 : public Class31 {
public:
// void get_a() { cout << a << '\n'; } // エラー
void get_b() { cout << b << '\n'; }
void get_c() { cout << c << '\n'; }
// ゲッタ get_a(), get_b(), get_c()は public の
// ままで継承されているので記述不要。
};
int main() {
Class00 c00;
// cout << c00.a << '\n'; // エラー
// cout << c00.b << '\n'; // エラー
cout << c00.c << '\n'; // 30
Class11 c11;
// cout << c11.a << '\n'; // エラー
// cout << c11.b << '\n'; // エラー
// cout << c11.c << '\n'; // エラー
Class21 c21;
// cout << c21.a << '\n'; // エラー
// cout << c21.b << '\n'; // エラー
// cout << c21.c << '\n'; // エラー
Class31 c31;
// cout << c31.a << '\n'; // エラー
// cout << c31.b << '\n'; // エラー
cout << c31.c << '\n'; // 30
Class12 c12;
Class22 c22;
Class32 c32;
c00.get_a(); // 10
c00.get_b(); // 20
c00.get_c(); // 30
c11.get_b(); // 20
c11.get_c(); // 30
c21.get_b(); // 20
c21.get_c(); // 30
c22.get_b(); // 20
c22.get_c(); // 30
c31.get_b(); // 20
c31.get_c(); // 30
c32.get_b(); // 20
c32.get_c(); // 30
}
2017年12月14日
《その177》 ポインタのアップキャスト・ダウンキャストの可否
クラス型オブジェクトへのポインタの、アップキャスト・ダウンキャストの可否を、下記のプログラムで確認してみました。
各クラスは次のような関係です。プログラムの実行結果は、コード中にメモ書きしました。
◆ 基底クラス Aaa
public派生による派生クラス Bbb
@ アップキャスト
A ダウンキャスト
◆ 基底クラス Ppp
private派生による派生クラス Qqq
B アップキャスト
C ダウンキャスト
// ------------------------------------
#include <iostream>
using namespace std;
class Aaa { // 基底クラス
int a1;
public:
Aaa(int x = 10) : a1(x) { }
int get_a1() const { return a1; }
};
class Bbb : public Aaa { // Aaa の子クラス
public:
Bbb(int x = 20) : Aaa(x) { }
};
// ------------------
class Ppp { // 基底クラス
int p1;
public:
Ppp(int x = 10) : p1(x) { }
int get_p1() const { return p1; }
};
class Qqq : private Ppp { // Ppp の子クラス
public:
Qqq(int x = 20) : Ppp(x) { }
};
int main() {
Aaa objA;
Bbb objB;
Aaa* ptr1 = &objA;
Aaa* ptr2 = &objB; // @
// Bbb* ptr3 = &objA; // Aエラー
// 型 "Aaa *" の値を使用して型 "Bbb *" のエンティティ
// を初期化することはできません。
Bbb* ptr4 = &objB;
// ------------------
Ppp objP;
Qqq objQ;
Ppp* ptr5 = &objP;
// Ppp* ptr6 = &objQ; // Bエラー
//アクセスできない基底クラス "Ppp" への変換は許可され
// ていません。
// Qqq* ptr7 = &objP; // Cエラー
// 型 "Ppp *" の値を使用して型 "Qqq *" のエンティティ
// を初期化することはできません。
Qqq* ptr8 = &objQ;
}
// ------------------------------------
《その176》 ポインタのアップキャスト(孫派生クラス→基底クラス)
下のプログラムで、派生クラス Bbb は、 基底クラス Aaa から private派生した子クラスです。
そして、クラス Ccc は、クラス Bbb から private派生したクラスです。したがって、基底クラス Aaa から見ればクラス Ccc は孫派生クラスということになります。
private 派生を2回経て、クラス Ccc のアクセス不可領域に格納された関数 get_a にアクセスするために、ポインタを Ccc*型から Aaa*型にアップキャストしてみました。
private派生なので、アップキャストの実行は不可と予想していましたが、実行可能でした。まだ、勉強が足りません (_ _|||);
// ------------------------------------
#include <iostream>
using namespace std;
class Aaa { // 基底クラス
int a1;
public:
Aaa(int x = 10) : a1(x) { }
int get_a1() const { return a1; }
};
class Bbb : private Aaa { // 派生クラス(private派生
public: // による Aaaの子クラス)
Bbb(int x = 20) : Aaa(x) { }
};
class Ccc : private Bbb { // 派生クラス(private派生
public: // による Aaaの孫クラス)
Ccc(int x = 30) : Bbb(x) { }
};
int main() {
Aaa objA;
Ccc objC;
Aaa* ptrA = &objA;
Ccc* ptrC = &objC;
// cout << ptrC->get_a1() << '\n'; // エラー
cout << ((Aaa*)ptrC)->get_a1() << '\n'; // 30
// Ccc型オブジェクト objC を指すポインタ ptrC は、
// objC の アクセス不可private領域に
// ある get_a1()を指すことはできませ
// んが、ポインタを Ccc*型から Aaa*型
// にアップキャストすることで、指すこ
// とができるようになります。
}
// ------------------------------------
2017年12月13日
《その175》 private派生 と public派生
private派生 と public派生
クラス継承の際の、private派生と public派生の相違点を、ここで少しだけ確認しておきたいと思います。
【private派生】
// ------------------------------------
#include <iostream>
using namespace std;
class Aaa { // ◆基底クラス
int a;
public:
Aaa(int n = 10)
: a(n) { }
int get_a() const { return a; }
void set_a(int n) { a = n; }
};
class Bbb : private Aaa { // ◆派生クラス
int b;
public:
Bbb(int n = 30)
: Aaa(n), b(99) { }
int get_b() const { return b; }
};
int main() {
Aaa a1;
// クラスオブジェクト a1 の中身
// 【private】
// ・非公開
// データメンバ a = 10
// 【public】
// ・公開
// メンバ関数 get_a()
// 〃 set_a()
Bbb b1;
// クラスオブジェクト b1 の中身
// 【private】
// ・アクセス不可
// データメンバ a = 30
// ・非公開
// メンバ関数 get_a()
// 〃 set_a()
// データメンバ b = 99
// 【public】
// ・公開
// メンバ関数 get_b()
cout << a1.get_a() << '\n'; // 10
// cout << b1.get_a() << '\n'; // エラー
// 関数"Aaa::get_a"にアクセスできません。
// Aaa* ptr = &b1; // エラー
// アクセスできない基底クラス"Aaa"への変換は
// 許可されていません。
Bbb* ptr = &b1;
// 派生クラス Bbb型オブジェクト b1 への
// ポインタ ptr を定義。
cout << ((Aaa*)ptr)->get_a() << '\n'; // 30
// ポインタ ptr を Aaa*型にアップキャストす
// ると、非公開メンバ関数 get_a()
// にアクセスできるようになります。
cout << b1.get_b() << '\n'; // 99
}
// ------------------------------------
【public派生】
// ------------------------------------
#include <iostream>
using namespace std;
class Aaa { // ◆基底クラス
int a;
public:
Aaa(int n = 10)
: a(n) { }
int get_a() const { return a; }
void set_a(int n) { a = n; }
};
class Bbb : public Aaa { // ◆派生クラス
int b;
public:
Bbb(int n = 30)
: Aaa(n), b(99) { }
int get_b() const { return b; }
};
int main() {
Aaa a1;
// クラスオブジェクト a1 の中身
// 【private】
// ・非公開
// データメンバ a = 10
// 【public】
// ・公開
// メンバ関数 get_a()
// 〃 set_a()
Bbb b1;
// クラスオブジェクト b1 の中身
// 【private】
// ・アクセス不可
// ※関数 get_a(), set_a() 以外
// からのアクセス不可
// データメンバ a = 30
// ・非公開
// データメンバ b = 99
// 【public】
// ・公開
// メンバ関数 get_a()
// 〃 set_a()
// 〃 get_b()
cout << a1.get_a() << '\n'; // 10
cout << b1.get_a() << '\n'; // 30
cout << b1.get_b() << '\n'; // 99
}
// ------------------------------------