新規記事の投稿を行うことで、非表示にすることが可能です。
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);
}
// ------------------------------------