アフィリエイト広告を利用しています

広告

この広告は30日以上更新がないブログに表示されております。
新規記事の投稿を行うことで、非表示にすることが可能です。
posted by fanblog

2017年12月17日

《その184》 仮想関数(2)


 仮想関数

 前回《183》のプログラムと今回のプログラムの相違点は、
基底クラス Member のメンバ関数の関数宣言 void print() const; の前に virtual を付加して、

  virtual void print() const;

としてある箇所( 印 )だけです。
これで、関数 print は仮想関数になります。

 派生クラスのメンバ関数で、基底クラスの仮想関数と仕様(関数名と仮引数)が同じものは、virtual が付加されていなくても仮想関数とみなされます。
したがって、Member2クラスのメンバ関数 print も仮想関数です。

 プログラムコードより実行結果が先になってしまいますが、実行結果は次のようになります。
上が前回《183》の実行結果、下が今回(メンバ関数 print が仮想関数)の実行結果です。

【 前回《183》の実行結果 】
f05_000207.png

【 今回の実行結果 】
f05_000307.png


 今回のプログラムは、関数 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();
}
// ------------------------------------





新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)





《その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();
}
// ------------------------------------

f05_000207.png


新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)





《その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';
}
// ------------------------------------

f05_0001.png


新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)





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
// にアクセスすることができます。

}
// ------------------------------------

f04_0011.png


新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)





《その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
}
// ------------------------------------

f04_0010.png



新版明解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';
}

f04_04.png


新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)





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();
}
// ------------------------------------

f04_0009.png


新版明解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
}
// ---------------------△


新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)





《その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
}


新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)





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;
}
// ------------------------------------


新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)





《その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*型
// にアップキャストすることで、指すこ
// とができるようになります。

}
// ------------------------------------
f04_0008.png


新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)





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
}
// ------------------------------------
f04_0006.png


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
}
// ------------------------------------
f04_0007.png


新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)






 たまに、クリック お願いします m(_ _)m

 AA にほんブログ村 IT技術ブログ C/C++へ

こうすけ:メール kousuke_cpp@outlook.jp

【1】★★C++ 記事目次★★ ← 利用可能です。
・新版明解C++入門編 / 新版明解C++中級編
・その他 C++ 関連記事

【2】★★こうすけ@C#★★
・C# の初歩的な記事


検索
<< 2017年12月 >>
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            
プロフィール
こうすけさんの画像
こうすけ

 たまに、クリック お願いします m(_ _)m

 AA にほんブログ村 IT技術ブログ C/C++へ

こうすけ:メール kousuke_cpp@outlook.jp

【1】★★C++ 記事目次★★ ← 利用可能です。
・新版明解C++入門編 / 新版明解C++中級編
・その他 C++ 関連記事

【2】★★こうすけ@C#★★
・C# の初歩的な記事


×

この広告は30日以上新しい記事の更新がないブログに表示されております。