C++においてキャストとは、ある型からある型への(明示的な) 変換操作を意味します。
そしてC言語ではキャストに()を使用しました。 次がC言語でのキャストです。
main() {
const double hoge = 2.75;
int bar = (int)hoge; // 型に()をつけても・・・
float piyo = float(hoge); // 変数につけてもよい
}C言語のキャストをC++で使用すると二つの欠点があります。
一つ目は、C言語風のキャストひとつでどんな型へでもキャストできてしまう点で、 二つ目は、そのキャストが何の目的で行われたかを全く示さない点です。
警告を消すためにわけも分からないままキャストを行ったりする人もいます。
C++で行われる主なキャストとして、実数型から整数型への変換のようなキャスト、
constやvolatileを除去するためのキャスト、
実行時まで詳細は分からない動的なキャストがあります。
C++をC言語風に使用した場合、ビット構成を再解釈するキャストも必要です。
これらを明確にするためにC++では4つのキャスト用のキーワード(キャスト演算子) が追加されました。
以下がC++で追加されたキャスト演算子です。
static_castconst_castreinterpret_castdynamic_castキャストの書式は全て同じで、"キャスト演算子<キャスト後の型名>(キャスト対象)" と記述します。
static_castは静的なキャストで、名前の通りコンパイル時にエラーチェックが行われ、
実行時にはすでに変換後の型は決定しています。
int型からdouble型や、
double型からfloat型といった、
一般的なキャストに使用します。
具体的には、暗黙的な変換が存在する場合にコンパイルが成功し、 キャストが不正な(キャストできない)場合はコンパイルエラーとなります。
int main(void) {
int hoge = 10;
double piyo = 2.25;
int piyopiyo = static_cast<int>(piyo);
hoge hogehoge = static_cast<double>(hoge);
}
constやvolatileを除去するためのキャストで、
このキャストも不正な場合はコンパイルエラーとなります。
int main(void) {
const int* HOGE;
int* hoge = const_cast<int*>(HOGE);
}
最も危険なキャストです。
このキャストは強制的にビット構成を変更するので、
void*の変換や整数型のポインタへの変換などに使用します。
このキャストは危険なので、使う気をなくすような長い名前が付けられています。
普通は使用しなくても大丈夫なのですが、Win32APIで一部使用する必要が出てきます。
int main(void) {
long hoge = 1111;
int* hogehoge = reinterpret_cast<int*>(hoge);
}
reinterpret_castはビット構成を再解釈するだけなので、
constやvolatileの除去は出来ませんし、
継承関係にあってもポインタは再計算されません。
このキャストは他のキャストとは違い、 キャスト可能かどうかを実行時に判断して可能ならキャスト後を、 不可能ならヌルポインタを返します。
class Hoge {
public:
virtual ~Hoge(void) {}
void func(void) {}
};
class ExHoge : public Hoge {};
class Piyo {};
int main(void) {
ExHoge* exHoge;
Piyo* piyo;
if (Hoge* hoge = dynamic_cast<Hoge*>(exHoge))
hoge->func();
else
return 1;
if (Hoge* hoge = dynamic_cast<Hoge*>(piyo))
hoge->func();
else
return 2;
}
上の例ではHogeを継承しているExHogeのキャストは成功し、
if文の条件式が非0(非ヌルポインタ)
になり、中身が実行されます。
Hogeを継承していないPiyoのキャストは失敗し、
if文の条件式が0(ヌルポインタ)
になるので、else文の中身が実行され、
システムの戻り値として2が返されます。
便利だと思うかもしれませんが、こういった処理にはポリモフィズムを使用すべきであり、
dynamic_castは従来のC風のキャストを使用したコードを安全に置き換える用途以外には使用すべきではありません。
もちろん使用しなければいけない場合もありますが、ほとんどの場合は設計ミスです。
変換に関係してexplicitキーワードについても書いておきます。
explicitキーワードは引数を一個しか持たないコンストラクタの暗黙の変換を禁止します。
例を見てもらったほうが分かりやすいでしょう。
class Hoge {
int hoge;
public:
Hoge(int hoge) { this->hoge = hoge; }
int getHoge(void) { return hoge; }
};
int main(void) {
Hoge hoge = 10; // OK
return hoge.getHoge();
}
Hogeクラスにexplicitを指定しておくと、
上のような動作を禁止できます。
class Hoge {
int hoge;
public:
explicit Hoge(int hoge) { this->hoge = hoge; }
int getHoge(void) { return hoge; }
};
int main(void) {
Hoge hoge = 10; // エラー
Hoge hoge2(20);
return hoge2.getHoge();
}