新規記事の投稿を行うことで、非表示にすることが可能です。
2017年12月25日
《その204》 抽象クラス(2)
抽象クラス
前回《203》、仮想関数からの派生関数 Rectangle を作成しました。
Rectangl関数は四角形クラスでしたが、今回は、横線クラス HorzLine と縦線クラス VertLine を作成します。
※ '-' で描く横線クラス … HorzLine
※ '|' で描く縦線クラス … VertLine
この2つの関数には、"線"という共通点があります。"線"ならば、その線の長さをデータメンバとして持っていなければなりません。
そこで、まず、直線クラス Line を考えます。線を描く方法は、横線と縦線で異なるので関数 draw は、ここではまだ定義できません。
class Shape {
public:
virtual void draw() = 0;
};
class Line : public Shape {
int length; // 線の長さ
public:
};
この Lineクラスは、Shapeクラスの public派生クラスですが、
基底クラス Shape の純粋仮想関数 draw をオーバーライドしていません。
そのため、Lineクラスにおいても、draw は純粋仮想関数のままです。
純粋仮想関数を有するクラスは、抽象クラスです。
したがって、Lineクラスは、まだ抽象クラスです。
次のプログラムでは、Lineクラスの public派生クラスとして、
横線クラス HorzLine,縦線クラス VertLine を定義しています。
// ------------------------------------
#include <iostream>
// 図形クラス(抽象クラス) Shape
class Shape {
public:
virtual ~Shape() = 0;
// 純粋仮想デストラクタ
virtual void draw() const = 0;
// 描画
// 純粋仮想関数
};
inline Shape::~Shape() { }
// 長方形クラス Rectangle
class Rectangle : public Shape {
int width; // 横幅
int height; // 高さ
public:
Rectangle(int w, int h)
: width(w), height(h) { }
void draw() const {
for (int i = 1; i <= height; i++) {
for (int j = 1; j <= width; j++)
std::cout << '*';
std::cout << '\n';
}
}
};
// 直線クラス(抽象クラス) Line
class Line : public Shape {
protected:
int length; // 線の長さ
public:
Line(int len) : length(len) { }
};
// 横線クラス HorzLine
class HorzLine : public Line {
public:
HorzLine(int len) : Line(len) { }
void draw() const {
for (int i = 1; i <= length; i++)
std::cout << '-';
std::cout << '\n';
}
};
// 縦線クラス VertLine
class VertLine : public Line {
public:
VertLine(int len) : Line(len) { }
void draw() const {
for (int i = 1; i <= length; i++)
std::cout << "|\n";
}
};
int main() {
Rectangle a(5, 3);
HorzLine c(20);
VertLine d(3);
a.draw(); std::cout << '\n';
c.draw(); std::cout << '\n';
d.draw();
}
// ------------------------------------
《その203》 抽象クラス(1)
抽象クラス
次のような、簡単なクラスを作るものとします。
※ '-' で描く横線クラス … HorzLine
※ '|' で描く縦線クラス … VertLine
※ '*' で描く四角形クラス … Rectangle
これらはすべて、図形ですから、まず具体的な個々の形には踏み込まずに、抽象的な
図形クラス … Shape
を作成することにします。
とは言っても、図形というだけでは、形に関してはまだ何もわかりません。ひとつ確かなことは、図形なら、"描く"作業が必ず必要なはずです。
そこで、次のようなクラスを作ってみます。draw関数は、"描く"ための関数(関数の中身は空っぽですが)です。
class Shape {
public:
virtual void draw() = 0;
// " = 0 " は純粋指定子
// この純粋指定子を付けて宣言された
// 仮想関数は、純粋仮想関数となりま
// す。
};
【 抽象クラス 】
クラス Shape には具体的な情報が含まれていないので、Shape型のオブジェクトを作ることは不可能です。
この Shape のようなクラスは抽象クラスと呼ばれ、具体的な図形クラスは、このクラスからの派生で実現するようにします。
次のプログラムでは、抽象クラス Shape から派生した Rectangleクラスの長方形オブジェクト a, b を生成して、それぞれのオブジェクトの draw関数で長方形を描いています。
// ------------------------------------
#include <iostream>
// 抽象クラス Shape
class Shape {
public:
virtual ~Shape() = 0;
// 純粋仮想デストラクタ
virtual void draw() const = 0;
// 描画
// 純粋仮想関数
};
inline Shape::~Shape() { }
// 長方形クラス Rectangle
// (抽象クラス Shape から派生)
class Rectangle : public Shape {
int width; // 横幅
int height; // 高さ
public:
Rectangle(int w, int h)
: width(w), height(h) { }
void draw() const {
for (int i = 1; i <= height; i++) {
for (int j = 1; j <= width; j++)
std::cout << '*';
std::cout << '\n';
}
}
};
int main() {
Rectangle a(5, 3);
Rectangle b(3, 4);
Shape& ref = b;
// Rectangle& ref = b; としても結果は同じで
// すが、図形の種類が増えてきて、様々な
// 処理をしなけらばならなくなったときの
// 効率を考えれば、Shape& とすべきです。
a.draw();
std::cout << '\n';
ref.draw();
}
// ------------------------------------
《その202》 仮想関数のいちばん基礎的な部分 のおさらい
仮想関数に関する、いちばん基礎的な事項を、
下記の2つのプログラムと プログラム中の注記で おさらいしてみました •̀д•́;)/
@【仮想関数の利用無し】の場合
// ------------------------------------
#include <iostream>
using namespace std;
/* 基底クラス */
class Aaa {
int a;
public:
Aaa(int x = 10) : a(x) { }
void get() { cout << a << '\n'; }
};
/* 派生クラス(クラス Aaa を public継承) */
class Bbb : public Aaa {
int b;
public:
Bbb(int x = 20, int y = 99)
: Aaa(x), b(y) { }
void get() { cout << b << '\n'; }
// 基底クラスの get と関数名が同じなので、
// 基底クラスの get は隠蔽される。
};
int main()
{
Bbb bbb;
// Bbb型オブジェクト bbb を生成。
Aaa& ref = bbb;
// クラス Bbb は多相的クラスではないので、
// ref は bbb への静的な Aaa&型参照。
ref.get(); // 20 が表示される。
bbb.get(); // 99 が表示される。
// 基底クラスの get は隠蔽されているので、
// 派生クラス内で定義された get
// が呼び出される。
bbb.Aaa::get(); // 20 が表示される。
// 基底クラス Aaa から継承した get関数に
// アクセス。
}
// ------------------------------------
A【仮想関数の利用有り】の場合
// ------------------------------------
#include <iostream>
using namespace std;
/* 基底クラス */
class Aaa {
int a;
public:
Aaa(int x = 10) : a(x) { }
virtual void get() { cout << a << '\n'; }
// virtual を付けて仮想関数にする。
// クラス Bbb の 関数 get も自動的
// に仮想関数になる。
// クラス Aaa, Bbb は、仮想関数をメンバ
// として持つので、多相的クラスです。
};
/* 派生クラス(クラス Aaa を public継承) */
class Bbb : public Aaa {
int b;
public:
Bbb(int x = 20, int y = 99)
: Aaa(x), b(y) { }
void get() { cout << b << '\n'; }
// 基底クラスの get と関数名・仮引数が同
// じ関数を、派生クラス内で定義。
// このことを、「オーバライドする」と
// 表現します。
// オーバライドした関数は、
// オーバライダと呼ばれます。
};
int main()
{
Bbb bbb;
// Bbb型オブジェクト bbb を生成。
Aaa& ref = bbb;
// クラス Bbb は多相的クラスなので、
// Aaa&型参照 ref の動的な型は Bbb&型。
ref.get(); // 99 が表示される。
bbb.get(); // 99 が表示される。
// 基底クラスの get は隠蔽されているので、
// 派生クラス内で定義された get
// が呼び出される。
bbb.Aaa::get(); // 20 が表示される。
// 基底クラス Aaa から継承した get関数に
// アクセス。
}
// ------------------------------------
2017年12月24日
《その201》 例外 bad_alloc,bad_typeid,bad_cast
例外 bad_alloc,bad_typeid,bad_cast
このブログで、いままでに出てきたことがある例外は、多分、bad_alloc,bad_typeid,bad_cast の3つです。ザっと再確認しておきたいと思います。
bad_alloc例外
bad_alloc例外は、exceptionクラスの派生クラスです。記憶域の確保に失敗したときに送出されます。
処理系定義の NTBS(Null-Terminated Byte String すなわち C言語の文字列)を返却する関数 what 等、いくつかのメンバ関数を持っています。
// ------------------------------------
#include <iostream>
using namespace std;
int main() {
unsigned n = 0;
while (1) {
try {
n++;
double* a = new double[10000];
// double[10000]用の領域確保を
// 繰り返します。確保し続
// けるだけで、解放しない
// ので、そのうちヒープ領
// 域が足りなくなり失敗し
// ます。
}
catch (bad_alloc e) {
// 送出された bad_allocを e が受け
// 取ります。
cout << "n = " << n << '\n';
cout << e.what() << '\n';
// bad_allocクラスのメンバ関数
// what を呼び出します。
return 1;
}
}
}
// ------------------------------------
bad_typeid例外
bad_typeid例外は、typeid演算子(型に関するさまざまな情報を提供)のオペランドが空ポインタのときに送出されます。
この typeid演算子の結果は、オブジェクト(オペランド)が多相的である場合には、そのオブジェクトの動的な型になります。
また、bad_typeid例外は、exceptionクラスの派生クラスであり、処理系定義の NTBS を返却する関数 what 等、いくつかのメンバ関数を持っています。
※ typeid演算子 の結果は、const type_info& です。type_info は、処理系が生成する型情報を表すためのクラスで、処理系定義の NTBS を返却する関数 name 等、いくつかのメンバ関数を持っています。
// ------------------------------------
#include <typeinfo>
#include <iostream>
using namespace std;
class Aaa {
public:
virtual ~Aaa() { }
};
int main() {
Aaa* ptr = NULL;
try {
typeid(*ptr).name();
// typeid に空ポインタを与えて、例
// 外を送出させます。
}
catch (bad_typeid e) {
// 送出された bad_typeid を e が受け取
// ります。
cout << e.what() << '\n';
// bad_typeid のメンバ関数 what を
// 呼び出します。
}
}
// ------------------------------------
bad_cast例外
bad_cast例外は、exceptionクラスの派生クラスです。動的なキャストを行う際、参照型へのキャストに失敗したときに送出されます。処理系定義の NTBS を返却する関数 what 等、いくつかのメンバ関数を持っています。
// ------------------------------------
#include <iostream>
using namespace std;
class Aaa {
public:
virtual ~Aaa() { }
};
class Bbb : public Aaa { };
int main()
{
Aaa a;
try {
dynamic_cast<Bbb&>(a);
// ダウンキャストです。例外bad_cast
// が送出されます。
}
catch (bad_cast e) {
// 送出された bad_cast を e が受け取り
// ます。
cout << e.what() << '\n';
// bad_cast のメンバ関数 what を呼
// び出します。
}
}
// ------------------------------------
《その200》 キャスト演算子 & p.207演習5-2
キャスト演算子
C++ の4種類のキャスト演算子を、次のプログラムのコード中で、簡単にまとめてみました。
// ------------------------------------
#include <iostream>
using namespace std;
class Base {
public:
virtual ~Base() { }
};
class Derived : public Base { };
int main()
{
// (1) 動的キャスト dynamic_cast<☆>(x)
// ・多相的クラスへのポインタや参照
// をアップ(ダウン)キャストする。
Derived d;
Base* pt = dynamic_cast<Base*>(&d);
cout << typeid(&d).name() << '\n';
cout << typeid(pt).name() << "\n\n";
// (2) 静的キャスト static_cast<☆>(x)
// ・暗黙の型変換でも扱える変換や、
// それに近い変換。
double x = 6.7;
int n = static_cast<int>(x);
cout << typeid(x).name() << '\n';
cout << typeid(n).name() << "\n\n";
// (3) 強制キャスト reinterpret_cast<☆>(x)
// ・やや無理筋と思えるような、型
// の"変更"的な変換
int* p = &n;
int a = reinterpret_cast<int>(p);
cout << typeid(p).name() << '\n';
cout << typeid(a).name() << "\n\n";
// (4) 定値性キャスト const_cast<☆>(x)
// ・const や volatile を付けたり
// 外したりする変換。
int const* pp = &n;
int* qq = const_cast<int*>(p);
cout << typeid(pp).name() << '\n';
cout << typeid(qq).name() << '\n';
}
// ------------------------------------
新版明解C++中級編 p.207 演習5-2
次の関数 senior_print の引数を、ポインタでなく参照に書きかえたプログラムを作成せよ。
// ------------------------------------
#include <iostream>
#include "Member.h"
#include "VipMember.h"
#include "SeniorMember.h"
using namespace std;
void senior_print(Member* p) {
Member* d = dynamic_cast<SeniorMember*>(p);
if (d)
d->print();
}
int main() {
Member kaneko("金子健太", 15, 75.2);
VipMember mineya("峰屋龍次", 17, 89.2, "会費全額免除");
SeniorMember susaki("州崎賢一", 43, 63.7, 3);
senior_print(&kaneko);
senior_print(&mineya);
senior_print(&susaki);
}
// ------------------------------------
◆以下が解答のプログラムです。
// p207_演習5-2
#include <iostream>
#include "Member.h"
#include "VipMember.h"
#include "SeniorMember.h"
using namespace std;
void senior_print(Member& r)
{
try {
dynamic_cast<SeniorMember&>(r);
r.print();
}
catch (bad_cast) { }
}
int main()
{
Member kaneko("金子健太", 15, 75.2);
VipMember mineya("峰屋龍次", 17, 89.2, "会費全額免除");
SeniorMember susaki("州崎賢一", 43, 63.7, 3);
senior_print(kaneko);
senior_print(mineya);
senior_print(susaki);
}
前回《199》の 演習5-1 のコードに、この 演習5-2 の関数 senior_print を書き加え、さらに main関数を、この解答の main関数と入れ替えたプログラムの実行結果は、次のようになります。長寿会員の情報のみが表示されています。
《その199》 ダウンキャストが可能になる条件 & p.207演習5-1
前回《198》のプログラムの結果から、次のような場合にはダウンキャストが可能である
ことがわかります。
◆ クラスオブジェクトを指すポインタをキャストした後のポインタ型が、そのオブジェクトを指すことができるポインタ型である場合。
( または、クラスオブジェクトの参照をキャストした後の参照型が、そのオブジェクトを参照することができる参照型である場合。)
新版明解C++中級編 p.207 演習5-1
次の会員クラス群 Member.h, VipMember.h, SeniorMember.h に、
自己紹介をする仮想関数 introduce を追加せよ。表示内容は自分で考えること。
// ------------------------------------
// 一般会員クラス Member.h
#ifndef ___Member
#define ___Member
#include <iostream>
#include <string>
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);
}
std::string name() const {
return full_name;
}
int no() const { return number; }
double get_weight() const {
return weight;
}
void set_weight(double w) {
weight = (w > 0) ? w : 0;
}
virtual void print() const {
std::cout << "No." << number
<< " : " << full_name
<< "(" << weight
<< "kg)\n";
}
};
#endif
// ------------------------------------
// ------------------------------------
// 優待会員クラス VipMember.h
#ifndef ___VipMember
#define ___VipMember
#include <string>
#include <iostream>
#include "Member.h"
class VipMember : public Member {
std::string privilege; // 特典
public:
VipMember(const std::string& name,
int no,
double w,
const std::string& prv
)
: Member(name, no, w) {
set_privilege(prv);
}
std::string get_privilege() const {
return privilege;
}
void set_privilege(
const std::string& prv
) {
privilege
= (prv != "") ? prv : "未登録";
}
void print() const {
std::cout << "No." << no()
<< " : " << name() << "("
<< get_weight() << "kg)"
<< "特典 : " << privilege
<< '\n';
}
};
#endif
// ------------------------------------
// ------------------------------------
// 長寿会員クラス SeniorMember.h
#ifndef ___SeniorMember
#define ___SeniorMember
#include <string>
#include <iostream>
#include "Member.h"
class SeniorMember : public Member {
int care_level; // 要介護度
public:
SeniorMember(
const std::string& name,
int no,
double w,
int level
)
: Member(name, no, w) {
set_care_level(level);
}
int get_care_level() const {
return care_level;
}
void set_care_level(int level) {
care_level
= (level >= 1 && level <= 5)
? level : 0;
}
void print() const {
std::cout << "No." << no()
<< " : " << name() << "("
<< get_weight() << "kg)"
<< "要介護度 … "
<< care_level << '\n';
}
};
#endif
// ------------------------------------
// ------------------------------------
#include <iostream>
#include "Member.h"
#include "VipMember.h"
#include "SeniorMember.h"
using namespace std;
int main() {
Member kaneko("金子健太", 15, 75.2);
VipMember mineya("峰屋龍次", 17, 89.2, "会費全額免除");
SeniorMember susaki("州崎賢一", 43, 63.7, 3);
}
// ------------------------------------
◆以下が解答のプログラムです。
// p207_演習5-1
// ------------------------------------
// 一般会員クラス Member.h
#ifndef ___Member
#define ___Member
#include <iostream>
#include <string>
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);
}
std::string name() const {
return full_name;
}
int no() const { return number; }
double get_weight() const {
return weight;
}
void set_weight(double w) {
weight = (w > 0) ? w : 0;
}
virtual void print() const {
std::cout << "No." << number
<< " : " << full_name
<< "(" << weight
<< "kg)\n";
}
virtual void introduce() const {
std::cout << "会員番号 " << no()
<< "番の " << name() << "と申し"
"ます。"
"体重は " << get_weight()
<< "kgです。\n";
}
};
#endif
// ------------------------------------
// ------------------------------------
// 優待会員クラス VipMember.h
#ifndef ___VipMember
#define ___VipMember
#include <string>
#include <iostream>
#include "Member.h"
class VipMember : public Member {
std::string privilege; // 特典
public:
VipMember(const std::string& name,
int no,
double w,
const std::string& prv
)
: Member(name, no, w) {
set_privilege(prv);
}
std::string get_privilege() const {
return privilege;
}
void set_privilege(
const std::string& prv
) {
privilege
= (prv != "") ? prv : "未登録";
}
void print() const {
std::cout << "No." << no()
<< " : " << name() << "("
<< get_weight() << "kg)"
<< "特典 : " << privilege
<< '\n';
}
void introduce() const {
std::cout << "会員番号 " << no()
<< "番の " << name() << "と申し"
"ます。"
"体重は " << get_weight()
<< "kgです。\n"
<< privilege << "の優待会員です。";
}
};
#endif
// ------------------------------------
// ------------------------------------
// 長寿会員クラス SeniorMember.h
#ifndef ___SeniorMember
#define ___SeniorMember
#include <string>
#include <iostream>
#include "Member.h"
class SeniorMember : public Member {
int care_level; // 要介護度
public:
SeniorMember(
const std::string& name,
int no,
double w,
int level
)
: Member(name, no, w) {
set_care_level(level);
}
int get_care_level() const {
return care_level;
}
void set_care_level(int level) {
care_level
= (level >= 1 && level <= 5)
? level : 0;
}
void print() const {
std::cout << "No." << no()
<< " : " << name() << "("
<< get_weight() << "kg)"
<< "要介護度 … "
<< care_level << '\n';
}
void introduce() const {
std::cout << "会員番号 " << no()
<< "番の " << name() << "と申し"
"ます。"
"体重は " << get_weight()
<< "kgです。\n"
<< "要介護度 " << care_level
<< "の長寿会員です。";
}
};
#endif
// ------------------------------------
// ------------------------------------
// 自己紹介の表示 p207_5-1.cpp
#include <iostream>
#include "Member.h"
#include "VipMember.h"
#include "SeniorMember.h"
using namespace std;
void func(const Member& m) {
cout << "みなさん、こんにちは。";
m.introduce();
cout << "よろしくお願いいたします。\n\n";
}
int main() {
Member kaneko("金子健太", 15, 75.2);
VipMember mineya("峰屋龍次", 17, 89.2, "会費全額免除");
SeniorMember susaki("州崎賢一", 43, 63.7, 3);
func(kaneko);
func(mineya);
func(susaki);
}
// ------------------------------------
2017年12月23日
《その198》 ダウンキャスト
ダウンキャスト
前回《197》のプログラムは、ダウンキャストの説明にも使えることに、あとで気付きました。
前回のプログラムで、dynamic_cast演算子が行っていたのは、アップキャストではなく、このブログでは まだ出てきていなかったダウンキャストです ;^ω^A
プログラムを、もう一度、下に載せました。前回と全く同じものです。
プログラムの、オブジェクトBase* から Aaa*型へのダウンキャスト★1.は、
・オブジェクト x1 は Aaa型なので、当然、可能
・オブジェクト x2 は Bbb型なので、当然、不可
・オブジェクト x3 は Aaa型なので、当然、可能
・オブジェクト x4 は Bbb型なので、当然、不可
です。
// ------------------------------------
#include <iostream>
using namespace std;
class Base {
public:
virtual ~Base() { }
};
class Aaa : public Base {
};
class Bbb : public Base {
};
void func(Base& x) {
Aaa* ptr = dynamic_cast<Aaa*>(&x);
// ★1. Base* から Aaa* へのダウンキャスト
//
// &x を Aaa* に変換可ならば ptr はアドレス値、
// 変換不可ならば ptr は空ポインタ。
if (ptr) // アドレス値のときは処理を実行
cout << "取扱いできます。→ 【次の処理】\n";
else // 空ポインタのときはメッセージのみ
cout << "取扱い不可\n";
}
int main() {
Aaa x1, x3;
Bbb x2, x4;
// Base から派生した x1 〜 x4 の現在の型が、万一
// 不明だとしても、関数 func に渡せ
// ば、関数側で判断してもらえる。
cout << "@ "; func(x1);
cout << "A "; func(x2);
cout << "B "; func(x3);
cout << "C "; func(x4);
}
// ------------------------------------
《その197》 dynamic_cast演算子の利用( 単純な例 ;^ω^A )
前回《196》確認したように、dynamic_cast演算子でポインタの型変換を行った際、キャストに失敗すると空ポインタが生成されます。
空ポインタが生成されると、どんなときに役に立つのかを考えてみたのですが、ごく単純な例しか思い付きませんでした ;^ω^A
次のプログラムでは、Aaa型のオブジェクトだけを処理したいのに、オブジェクト x1 〜 x4 の型が不明であるものとします(プログラムを読めば、実際は不明ではありませんが ^^;)。
このプログラムでは、x1 〜 x4 を、とりあえず関数 func に渡して、関数側で、処理の対象かどうか判断してもらっています。
ただし、すべてのオブジェクトは、型 Base から派生したものであるということだけは分かっているものとします。
// ------------------------------------
#include <iostream>
using namespace std;
class Base {
public:
virtual ~Base() { }
};
class Aaa : public Base {
};
class Bbb : public Base {
};
void func(Base& x) {
Aaa* ptr = dynamic_cast<Aaa*>(&x);
// &x を Aaa* に変換可ならば ptr はアドレス値、
// 変換不可ならば ptr は空ポインタ。
if (ptr) // アドレス値のときは処理を実行
cout << "取扱いできます。→ 【次の処理】\n";
else // 空ポインタのときはメッセージのみ
cout << "取扱い不可\n";
}
int main() {
Aaa x1, x3;
Bbb x2, x4;
// Base から派生した x1 〜 x4 の現在の型が、万一
// 不明だとしても、関数 func に渡せ
// ば、関数側で判断してもらえる。
cout << "@ "; func(x1);
cout << "A "; func(x2);
cout << "B "; func(x3);
cout << "C "; func(x4);
}
// ------------------------------------
《その196》 dynamic_cast演算子 と 動的キャスト
動的キャスト
ここでは、
@ 「派生クラスへのポインタ」から「基底クラスへのポインタ」への型変換(アップキャスト)
A 「派生クラスへの参照」から「基底クラスの参照」への型変換(アップキャスト)
についてチェックします。
dynamic_cast演算子
dynamic_cast演算子は、多相的クラスの動的キャストを行う演算子です。
◆キャストに失敗した際の、dynamic_cast演算子の挙動
@ ポインタの場合
キャストにより生成されるポインタは、変換先の型をもつ空ポインタとなります。
A 参照の場合
例外 bad_cast が送出されます。
// ------------------------------------
#include <iostream>
using namespace std;
class Base {
public:
virtual ~Base() { }
// Base を多相的クラスにするために virtual
// を付けました。
};
class Derived : public Base { };
int main()
{
Base b;
Derived d;
/* ダウンキャスト(ポインタ) */
cout
<< "Derived* p1 = dynamic_cast<Derived*>(&b);\n";
Derived* p1 = dynamic_cast<Derived*>(&b);
cout << "&b … " << &b << '\n';
cout << "p1 … " << p1 << "\n\n";
/* アップキャスト(ポインタ) */
cout
<< "Base* p2 = dynamic_cast<Base*>(&d);\n";
Base* p2 = dynamic_cast<Base*>(&d);
cout << "&d … " << &d << '\n';
cout << "p2 … " << p2 << "\n\n";
/* ダウンキャスト(参照) */
cout
<< "Derived& r1 = dynamic_cast<Derived&>(b);\n";
try {
Derived& r1 = dynamic_cast<Derived&>(b);
cout << "成功\n\n";
}
catch (bad_cast e) {
cout << e.what() << "\n\n";
// bad_castクラスのメンバ関数 what()
}
/* アップキャスト(参照) */
cout
<< "Base& r2 = dynamic_cast<Base&>(d);\n";
try {
Base& r2 = dynamic_cast<Base&>(d);
cout << "成功\n";
}
catch (bad_cast e) {
cout << e.what() << '\n';
}
}
// ------------------------------------
2017年12月22日
《その195》 例外クラス bad_typeid
bad_typeidクラス
typeid演算子は、多相的クラス型へのポインタによる参照を受け取ると、その参照先の動的なオブジェクト型を表す type_infoオブジェクトを生成します。
そして、その type_infoオブジェクトへの参照が typeid演算子の演算結果です。
typeid演算子が、多相的クラス型へのポインタによる参照を受け取った際に、そのポインタが空ポインタであった場合には、例外 bad_typeid が送出されます。
bad_typeidクラスは、exceptionクラスの派生クラスで、<typeinfo>ヘッダで提供されます。
次のプログラムは、以上のことを確認するためのものです。
なお、多相的クラスオブジェクトの動的な型を調べる際には、各オブジェクトに埋め込まれた仮想関数テーブルへのポインタが利用されます。
// ------------------------------------
#include <exception>
#include <typeinfo>
#include <iostream>
using namespace std;
class Aaa {
public:
virtual void f() { }
// クラス Aaa を多相的クラスにするために、
// 仮想メンバ関数を設定。
};
int main() {
Aaa aaa;
// Aaa型のオブジェクト aaa を作成。
Aaa* ptr1 = &aaa;
// Aaa*型ポインター ptr1 は aaa を指す。
Aaa* ptr2 = NULL;
// Aaa*型ポインター ptr2 は NULL
try {
cout << typeid(*ptr1).name() << endl;
}
catch (bad_typeid e) {
cout << e.what() << endl;
}
try {
cout << typeid(*ptr2).name() << endl;
}
catch (bad_typeid e) {
cout << e.what() << endl;
// what は bad_typeidクラスのメンバ
// 関数であり、処理系定義の
// NTBS(C言語形式の文字列)
// を返却します。
}
}
// ------------------------------------