2006年02月03日

4種類の新しいキャスト

キャストは使わずに済ませられるならそれに越したとはないのですが、どうしても使わざるを得ないシチュエーションは少なからず存在します。

ならばせめて少しでも安全にキャストするために新たに定められたキャスト構文が、以下に挙げる static_cast / reinterpret_cast / const_cast / dynamic_cast です。

* static_cast

static_cast(expr)

static_castはexprの型からtypeへの暗黙の型変換、あるいはtypeからexprへの暗黙の型変換が存在する場合にだけキャストします。キャスト不可能であればコンパイルエラーとなります。

// int から long へ
int ival;
long lval = static_cast(ival);

reinterpret_cast

reinterpret_cast(expr)

reinterpret_castはtype(expr)が許されるなら、exprをtypeに単にキャストします。

// long から int* へ
long lval;
int* iptr = reinterpret_cast(lval);

reinterpret_castは単なる型変更であり、たとえ派生関係があったとしてもポインタのアドレス自体はキャスト前と変わりません。
その意味でreinterpret_castは非常に危険なキャストといえるでしょう。

const_cast

const_cast(expr)

const_castはconstおよびvolatile修飾子を無効にするだけのキャストを行ないます。そのほかのときはコンパイルエラーとします。

// const int* から int* へ
const int* ciptr;
int* iptr = const_cast(ciptr);

dynamic_cast

dynamic_cast(expr)

dynamic_castは基底クラスへのポインタ(or 参照)から派生クラスへのポインタ(or 参照)への型保証キャストを行ないます。
上記3種のキャストはコンパイル時にキャストしますが、dynamic_castは実行時に型の検査が行なわれ、変換不可能であれば0を返します。



class Base { ... };
class Derived : public Base { ... };

Base base;
Derived derived;

Derived* pd1 = dynamic_cast(&base); // 失敗 pd1 == 0
Derived* pd2 = dynamic_cast(&derived);


参照のキャストに失敗すると、std::bad_cast例外がthrowされます。

try {
Derived& rd1 = dynamic_cast(base);
} catch ( const std::bad_cast& e ) {
std::cout << e.what() << std::endl;
};

dynamic_castにより、従来のキャストでは不可能であった クロス・キャスト、そして抽象基底クラスからのダウン・キャストが可能になりました。

*
o クロス・キャスト

/*
* Shape と Drawable は派生関係にない
*/
class Shape {
...
};

class Drawable {
public:
virtual void draw() =0;
};

/*
* Circle は Drawable にキャスト可能
*/
class Circle : public Shape, public Drawable {
public:
virtual void draw();
};

Shape* shape = new Circle;
/* 従来のキャスト (Drawable*)shape では、
* 場合によっては暴走する */
Drawable* drawable = dynamic_cast(shape);
if ( drawable )
drawable->draw();


o 抽象基底クラスからのダウン・キャスト

class Machine {
...
};

class TV : virtual public Machine {
public:
void tune(int channel);
};

class VTR : virtual public Machine {
public:
void record();
};

class TeleVideo : public TV, public VTR {
...
};

Machine* machine = new TeleVideo;
/* 従来のキャスト (TeleVideo*)machine はコンパイルエラー */
TeleVideo* televideo = dynamic_cast(machine);
if ( televideo ) {
televideo->tune(8);
televideo->record();
}



※注意
dynamic_castが適用できるのはポリモルフィック・クラス、 すなわち少なくとも一つのメンバ関数が仮想関数でなくてはなりません。

struct non_polymorpic { ... }; // 仮想関数が存在しない
struct something : non_polumorphic { ... };
non_polymorphic* p;
something* q = dynamic_cast(p); // error!

新しいキャストは従来のキャストよりはるかに安全です。

また、grepなどで"_cast"をキーにすればキャストした個所を簡単に検索できるという副次的な効果もあります。
posted by シンビアン at 16:44| Comment(0) | TrackBack(0) | Symbian OS C++ 実践開発技法 | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
この記事へのトラックバックURL
http://blog.seesaa.jp/tb/12697076

この記事へのトラックバック
×

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