C++ のキャスト

C++ には 4 つのキャストがあります。

名称 説明 使用
dynamic_cast 基本クラスから派生クラスへのキャスト使用するべきではない
const_cast const を外すキャスト使用するべきではない
static_cast double から int など暗黙の変換のあるキャスト 使用可
reinterpret_cast double * から long long などの無理やりキャスト なるべく使用しない

個人的には、 C++ のキャストは書くのがめんどくさいし、 static_cast 以外はあまり使うべきではないのに static_cast は C のキャストだしってことで、 C 形式のキャストでいいんじゃねって思っています。
ただし、コーディング規約で決まっている場合などがあって使わないといけない場合とかもあると思います。 そこで、使い方を考えてみます。

書き方

C 形式のキャストの場合は対象に括弧を付ける必要はないのですが、 C++ 形式の場合は必ず付ける必要があります。
ival = (int)dval;               /* C */
ival = static_cast<int>(dval);  // C++
C++ のキャストは形式としては言語の機能というよりも templete 関数の一種のような見た目になってます。めんどくさいのですが、 Boost の lexical_cast のようにキャストっぽい関数をつくって形式を合わせることができるという利点もあります。

コードの見え方とコードの質

C に比べ、 C++ のキャストでは確かに書くのがめんどくさいなりましたが、あまりやるべきではないことはめんどくさくするのがいいという考え方もあります。

例えば Ruby ではグローバル変数やクラス変数には必ず $, @@ を付ける必要があります。こういったものは沢山使っているとコードが汚く見えます。 グローバル変数などを大量に使うことはコードの質としてもダメです。
コードの質の悪いものは汚く見えるので、綺麗に見えるようにコードを書きます。 そうすれば、自動的にコードの質も上がってきます。

C++ でのキャストの使用

C++ のキャストも、 C++ の型安全性を無理やり壊すので、よくないものだと言われています。(『 Effective C++ 』)
めんどくさいものは使いたがらなくなりますし、 C++ のキャストの汚さやめんどくささはいいことかも知れません。
もちろん、 めんどくさいから C 形式のキャストを使うということになるとだめなので、コーディング規約などで使用を強制しておく必要があります。

最初に言っていたことと違うことになってますが、一概にキャストはダメという訳ではないので、次回からこのキャストについて詳しく考えていきたいと思います。

Effective C++ 原著第 3 版 (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)Effective C++ 原著第 3 版 (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)
(2006/04/29)
スコット・メイヤーズ

商品詳細を見る

Boost C++ Libraries プログラミングBoost C++ Libraries プログラミング
(2007/07)
稲葉 一浩

商品詳細を見る

オブジェクト指向スクリプト言語 Ruby (ASCII SOFTWARE SCIENCE Language)オブジェクト指向スクリプト言語 Ruby (ASCII SOFTWARE SCIENCE Language)
(1999/10)
まつもと ゆきひろ、石塚 圭樹 他

商品詳細を見る
 

C++ のキャスト - dynamic_cast

今回は C++ のキャスト dynamic_cast について考えてみます。


C++ の 4 つのキャストのうち、 dynamic_cast 以外はもともと C でできるキャストを用途別に名前をつけただけなのですが、 dynamic_cast だけは C++ 特有のキャストです。
これは ダウンキャスト とも呼ばれるもので、基本型のポインターを派生型のポインターに変換する時に使うキャストです。

多様性

C++ のキモに多様性があります。例えば以下のような関係のクラスがあったとします。

class_figuire.png

Figuire クラスから各種の図形クラスが継承されています。 アプリケーションでは実際には Rect, Line などのオブジェクトを持っているのですが、それらを統一して扱うため、 抽象 クラスの Figuire 型としてオブジェクトのポインターを使います。

Figuire クラスのポインターから仮想関数 draw() を呼び出したとします。 多様性は、 Figuire クラスの draw() ではなく、実際のクラスの draw() が呼び出されるというものです。
これは Figuire 型のポインター を vector などにいれ、ループで回したりして使います。
for (itr = figs.begin() ; itr != figs.end() ; itr++)
{
    (*itr)->draw();
}
これを C で書こうとすると union などで統一したデータに入れることは出来ますが、 描画処理は対象ごとにわけないといけないので switch 文の山となります。 そのため、新しい図形クラスを追加した場合などあちこち修正する必要があります。

アップキャスト

多様性を使うため、Rect 型などの実際の具象クラスのオブジェクトのポインターを抽象 Figuire クラスのポインターに代入する必要があります。ここで発生するキャストはアップキャストと呼ばれます。
Figuire *fig = new Rect();
型の変換が起こりますが、キャストを明示的に示す必要はありません
Figure クラスでできることは必ず Rect クラスでもできるので安全ですし、多様性は C++ のキモの一つですので、コンパイラーは「当然このような使われ方はする」と思ってます。

ダウンキャスト(dynamic_cast)

アップキャストに対して、逆の Figure から Rect に変換するダウンキャストは危険です。
Rect *rect = dynamic_cast<Rect *>(somefig);
Figure クラスのポインター somefig の指す実態は Rect のオブジェクトかも知れませんし、 Line のオブジェクトかも知れません。 そのため、明示的にキャストを記述する必要があります


この際、 dynamic_cast を使っていると本当に Rect クラスのオブジェクト以外の場合は rect には NULL が入ります。
この dynamic_cast は安全にキャストしてくれるいいものなのですが、 「dynamic_cast は使うな」と言われています。

dynamic_cast を避ける理由

何故 dynamic_cast を避けるべきかというと、 そもそも抽象クラスのオブジェクトを具象クラスのオブジェクトを入れるダウンキャスト自体がよくありません。

dynamic_cast でキャストして処理を分けるのは、 C の switch 文で型別に処理を振り分けているようなものです。
これを避けるための多様性なので、 dynamic_cast で行う処理は仮想関数を使って記述するべきです。 dynamic_cast が必要な場合には「設計をまちがったかな」と思った方がいいでしょう。


このような話は『 Efefective C++ 』の 27 項に詳しく書かれているので、興味ある方は読んでみてください。

Effective C++ 原著第 3 版 (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)Effective C++ 原著第 3 版 (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)
(2006/04/29)
スコット・メイヤーズ

商品詳細を見る
 

C++ のキャスト - const_cast

今回は C++ のキャスト のうち、 const_cast を見てみます。

 const char *str = "Hello";
 char *ptr = const_cast<char *>(str);
これは const 修飾子をはずすためのキャストで、使うべきキャストではありません。変えないと宣言しているのを変えるためのものですから、なぜ使うべきではないかは明らかでしょう。
逆に const の方にキャストするのは、変えても変えなくてもいいものを変えないと宣言するだけなので、何も問題ありませんし、明示的にキャストする必要もありません。



では、どうしても const を外さないといけない場面はあるでしょうか。
私は必要な場面は 2 つ程あると思います。


まず、一つは Motif などの古くからあるライブラリーを使っている場合です。
const に対応していないダメなインターフェースなのに、 互換性などのために引きづられて残っていることがあります。 こういった場合には const_cast を使わざるを得ません。


あとは、次のようなポインターを返す関数の場合です。
char *case_strchr(const char *str, char chr)
{
    char lch = tolower(chr);
    for (; *str ; str++) {
        if (tolower(*str) == lch)
            return const_cast<char *>(str);
    }
    return NULL;
}
この場合は、引数として受け取ったものを使った側に返しているだけなので、問題はないですし、 const char * を返すようなインタフェースにしてしまうと使いづらいものになります。

ただし、 これは C 形式の関数ですし、使おうと思えば const 外しの関数としても使えるようになってしまいます。 C++ の標準ライブラリのように位置やイテレータを返すようなものを作った方がいいと思います。

STL 標準講座―標準テンプレートライブラリを利用した C++ プログラミング (Programmer ’ s SELECTION)STL 標準講座―標準テンプレートライブラリを利用した C++ プログラミング (Programmer ’ s SELECTION)
(1999/08)
ハーバート シルト

商品詳細を見る

Effective STL ― STL を効果的に使いこなす 50 の鉄則Effective STL ― STL を効果的に使いこなす 50 の鉄則
(2002/01)
スコット メイヤーズ

商品詳細を見る
 
このページをシェア
アクセスカウンター
アクセスランキング
[ジャンルランキング]
コンピュータ
33位
アクセスランキングを見る>>

[サブジャンルランキング]
プログラミング
3位
アクセスランキングを見る>>
カレンダー(アーカイブ)
プルダウン 降順 昇順 年別

04月 | 2017年05月 | 06月
- 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 - - -


はてな新着記事
はてな人気記事
ブロとも申請フォーム
プロフィール

yohshiy

Author:yohshiy
職業プログラマー。
仕事は主に C++ ですが、軽い言語マニアなので、色々使っています。

はてブ:yohshiy のブックマーク
Twitter:@yohshiy

サイト紹介
プログラミング好きのブログです。プログラミング関連の話題や公開ソフトの開発記などを雑多に書いてます。ただ、たまに英語やネット系の話になることも。