新規記事の投稿を行うことで、非表示にすることが可能です。
2017年12月29日
《その212》 アップキャストとポインタ & p.245演習7-2
アップキャストとポインタ
下のプログラムにおいて、、
・派生クラス C は、基底クラス A からの単一継承クラス、
・派生クラス D は、基底クラス A, B からの多重継承クラスです。
C c; // C型のオブジェクト c を生成
A* ptr1 = &c; // (1)
C* ptr2 = &c; // (2)
(1) 派生オブジェクト c を指すポインタの値 &c を、A* ptr1 に代入しているので、C*型から A*型へのアップキャストが行われています。
(2) &c を C* ptr2 に代入しています。キャストの必要はないので、ptr2 と &c は、当然、同じ値になります。
プログラムの出力結果を見ると、ptr1, ptr2, &c の値は、すべて同じになっています。
D d; // D型のオブジェクト d を生成
A* ptr3 = &d; // (3)
B* ptr4 = &d; // (4)
D* ptr5 = &d; // (5)
(3) 多重継承による派生オブジェクト d を指すポインタの値 &d を、A* ptr3に代入しているので、D*型から A*型へのアップキャストが行われています。
(4) 多重継承による派生オブジェクト d を指すポインタの値 &d を、B* ptr4に代入しているので、D*型から B*型へのアップキャストが行われています。
(5) &d を D* ptr5 に代入しています。キャストの必要はないので、ptr5 と &d は、当然、同じ値になります。
プログラムの出力結果を見ると、多重継承の場合は、ポインタの値が、&dとは違う値に変化する場合があることがわかります。
// ------------------------------------
#include <iostream>
using namespace std;
class A { };
class B { };
class C : public A { };
class D : public A, public B { };
int main() {
C c;
D d;
A* ptr1 = &c; // (1)
C* ptr2 = &c; // (2)
A* ptr3 = &d; // (3)
B* ptr4 = &d; // (4)
D* ptr5 = &d; // (5)
cout << "&c " << &c << '\n';
cout << "(1) " << ptr1 << '\n';
cout << "(2) " << ptr2 << "\n\n";
cout << "&d " << &d << '\n';
cout << "(3) " << ptr3 << '\n';
cout << "(4) " << ptr4 << '\n';
cout << "(5) " << ptr5 << '\n';
}
// ------------------------------------
新版明解C++中級編 p.245 演習7-2
次のクラス Derived にメンバ関数 print を追加せよ。なお、メンバ関数 print は、以下の表示を行うものとする。
[1] Base1クラスです : x = 1
[2] Base2クラスです : x = 2
[3] Derivedクラスです : y = 3
なお、[1] と [2] は、下記のプログラムで既に出力されるようになっているので、[3] の表示のみ追加すること。
// ------------------------------------
#include <iostream>
using namespace std;
// 基底クラス1
class Base1 {
public:
int x;
Base1(int a = 0) : x(a) {
cout << "Base1::xを" << x
<< "に初期化しました。\n";
}
void print() {
cout << "Base1クラスです:x = "
<< x << '\n';
}
};
// 基底クラス2
class Base2 {
public:
int x;
Base2(int a = 0) : x(a) {
cout << "Base2::xを" << x
<< "に初期化しました。\n";
}
void print() {
cout << "Base2クラスです:x = "
<< x << '\n';
}
};
// 派生クラス
class Derived
: public Base1, public Base2 {
int y;
public:
Derived(int a, int b, int c)
: y(c), Base2(a), Base1(b) {
cout << "Derived::yを" << y
<< "に初期化しました。\n";
}
void func(int a, int b) {
Base1::x = a;
Base2::x = b;
}
};
int main()
{
Derived z(1, 2, 3);
z.func(1, 2);
z.Base1::print();
z.Base2::print();
}
// ------------------------------------
以下が、解答のプログラムです。
// ------------------------------------
#include <iostream>
using namespace std;
// 基底クラス1
class Base1 {
public:
int x;
Base1(int a = 0) : x(a) {
cout << "Base1::xを" << x
<< "に初期化しました。\n";
}
void print() {
cout << "Base1クラスです:x = "
<< x << '\n';
}
};
// 基底クラス2
class Base2 {
public:
int x;
Base2(int a = 0) : x(a) {
cout << "Base2::xを" << x
<< "に初期化しました。\n";
}
void print() {
cout << "Base2クラスです:x = "
<< x << '\n';
}
};
// 派生クラス
class Derived
: public Base1, public Base2 {
int y;
public:
Derived(int a, int b, int c)
: y(c), Base2(a), Base1(b) {
cout << "Derived::yを" << y
<< "に初期化しました。\n";
}
void func(int a, int b) {
Base1::x = a;
Base2::x = b;
}
void print() {
cout << "Derivedクラスです:y = "
<< y << '\n';
}
};
int main()
{
Derived z(1, 2, 3);
z.func(1, 2);
z.Base1::print();
z.Base2::print();
z.print();
}
// ------------------------------------
《その211》 多重継承 & p.244演習7-1
多重継承
次の3つのクラスがあるものとします。C は、2つの基底クラス A, B からの派生クラスです。
その結果、C は、2つの基底クラスの資産を継承することになります。このような継承の形を、多重継承と呼びます。
【クラス A】
コンストラクタ … 「 A 」 を表示
デストラクタ … 「~A 」 を表示
データメンバ x = 1
メンバ関数 get_x() … x の値を返却
【クラス B】
コンストラクタ … 「 B 」 を表示
デストラクタ … 「~B 」 を表示
データメンバ x = 1
メンバ関数 get_x() … x の値を返却
【クラス C】基底クラス A, B を多重継承
コンストラクタ … 「 C 」 を表示
デストラクタ … 「~C 」 を表示
データメンバ x = 1
メンバ関数 get_x() … x の値を返却
次のプログラムの結果から、コンストラクタが呼び出される順序と、デストラクタが呼び出される順序が確認できます。
また、クラス C は、
基底クラス部分オブジェクト A のデータメンバ x
基底クラス部分オブジェクト B のデータメンバ x
派生クラス C のデータメンバ x
の3つの x を保持することになります。
これらに、get_x関数でアクセスする際には、
c.A::get_x();
c.B::get_x();
c.get_x();
のように、明確な指定が必要になります。
// ---------------------------
#include <iostream>
using namespace std;
class A {
protected:
int x;
public:
A() { cout << "A" << '\n'; x = 1; }
~A() { cout << "~A" << '\n'; }
int get_x() const { return x; }
};
class B {
protected:
int x;
public:
B() { cout << "B" << '\n'; x = 2; }
~B() { cout << "~B" << '\n'; }
int get_x() const { return x; }
};
class C : public A, public B {
protected:
int x;
public:
C() { cout << "C" << '\n'; x = 3; }
~C() { cout << "~C" << '\n'; }
int get_x() const { return x; }
};
int main() {
A a;
cout << "------\n";
B b;
cout << "------\n";
C c;
cout << "------\n";
cout << c.A::get_x() << ' '
<< c.B::get_x() << ' '
<< c.get_x() << '\n';
cout << "------\n";
}
// ------------------------------------
新版明解C++中級編 p.244 演習7-1
以下のクラス群のクラス階層図を描き、クラス Z型のオブジェクトを初期化する際の基底クラス部分オブジェクトの初期化の順序を示せ。
class A{ /* ・・・・・・ */ };
class B{ /* ・・・・・・ */ };
class X : A, B{ /* ・・・・・・ */ };
class Y : B, A{ /* ・・・・・・ */ };
class Z : X, Y{ /* ・・・・・・ */ };
※アクセス指定子 public, protected, private が記述されていない場合は、private派生と解釈されます。
クラス階層図
Z型のオブジェクトを初期化する際の規定クラス部分オブジェクトの初期化の順序は、次のプログラムの出力結果の2本の "------"線の間で確認できます。
// ---------------------------
#include <iostream>
class A {
public:
A() { std::cout << "A" << '\n'; }
};
class B {
public:
B() { std::cout << "B" << '\n'; }
};
class X : A, B {
public:
X() { std::cout << "X" << '\n'; }
};
class Y : B, A {
public:
Y() { std::cout << "Y" << '\n'; }
};
class Z : X, Y {
public:
Z() { std::cout << "Z" << '\n'; }
};
int main() {
A a;
B b;
X x;
Y y;
std::cout << "\nZ型のオブジェクトを初期化"
"する際の \n規定クラス部分オ"
"ブジェクトの初期化の順序 \n"
"↓\n------\n";
Z z;
std::cout << "------\n";
}
// ------------------------------------