明示的型変換とは、ある型を別の型に明確に変換することを指す。
参照
C++における明示的なデータ型変換
従来の強制型変換と比較して、命名された強制型変換は形式的により明確で、見落としにくいという利点があるため、変換中に問題が発生した場合に追跡が困難になることはない。
C++は新しい型変換メカニズムを導入することで、C言語の強制型変換の以下の3つの欠点を克服した:
- 変換機能とリスクの違いを形式的に表現していない。
- ポリモーフィックな基底クラスのポインタから派生クラスのポインタへの変換時に安全性をチェックしない。
- プログラム内でどこで型変換が行われたかを特定するのが難しい。
1. static_cast
これはコンパイル時に確定される静的な変換であり、C言語での強制型変換の大部分を処理できるが、const、volatile、または__unaligned属性は変換されないことに注意が必要である。
- 大きな算術型から小さな算術型への変換
double slope = static_cast<double>(j) / i;
これは (double)j/i に相当する。
#include <iostream>
using namespace std;
class A {
public:
operator int() { return 1; }
operator char*() { return NULL; }
};
int main() {
A a;
int n;
char* p = "New Dragon Inn";
n = static_cast<int>(3.14); // n は 3 になる
n = static_cast<int>(a); // a.operator int を呼び出し、n は 1 になる
p = static_cast<char*>(a); // a.operator char* を呼び出し、p は NULL になる
n = static_cast<int>(p); // コンパイルエラー、static_cast はポインタから整数への変換はできない
p = static_cast<char*>(n); // コンパイルエラー、static_cast は整数からポインタへの変換はできない
return 0;
}
- constの追加:非constからconstへの変換
- 式をvoid型に変換し、結果を破棄
int val = 110;
static_cast<void>(val);
- コンパイラが自動的に実行できない型変換にも有用
void* p = &d;
double* dp = static_cast<double*>(p);
- 異なる型のポインタ間(例:char* と int*)や整数とポインタ間の変換は不可
- クラス継承構造において、基底クラスと派生クラスのポインタや参照の変換に使える。上位変換は安全だが、下位変換は動的型チェックがないため危険
- 左値から右値、配列からポインタ、関数からポインタへの変換もstatic_castで明示的に実行可能
2. dynamic_cast
基底クラスのポインタまたは参照を使用して派生クラスの操作を実行したい場合に使用される。
- コンパイル時ではなく実行時に型チェックを行う
- 継承関係を持つクラス間でのみ変換可能
- 基底クラスに仮想関数が必要
- void*型への変換も可能
- RTTI(実行時型識別)
実行時型識別(RTTI)は、以下の2つの演算子によって実現される:
- typeid演算子:式の型を返す
- dynamic_cast演算子:基底クラスのポインタまたは参照を派生クラスのポインタまたは参照へ安全に変換する
- 上位変換(Upcasting)と下位変換(Downcasting)の両方をサポート
- 3種類の使用形式
- 型変換の条件:
- eの型が目標型の公開派生クラス
- eの型が目標型の公開基底クラス
- eの型が目標型そのもの
dynamic_cast<type*>(e)// eは有効なポインタであることdynamic_cast<type&>(e)// eは左辺値であることdynamic_cast<type&&>(e)// eは左辺値ではないこと
3. const_cast
定数オブジェクトを非定数オブジェクトに変換する。つまり、const属性を外す。
- constまたはvolatile修飾子を取り除く
- 変数の定数性を変更することはできず、ポインタまたは参照の定数性のみ変更可能
#include <iostream>
using namespace std;
int main(void) {
const int d = 2;
int* a = const_cast<int*>(&d);
*a = 3;
cout << d << endl;
return 0;
}
出力結果:
2
これはコンパイル時にdの使用箇所が2に置き換えられているため。
- const参照から非const参照、constポインタから非constポインタへの変換に使用可能
#include <iostream>
using namespace std;
int main(void) {
int d = 2;
const int* a = &d;
int* b = const_cast<int*>(a);
cout << *a << endl;
cout << *b << endl;
*b = 3;
cout << *a << endl;
cout << *b << endl;
return 0;
}
出力結果:
2
2
3
3
4. reinterpret_cast
- 異なる型のポインタや参照、ポインタと整数間の変換に使用される
- オペランドのビットパターンを再解釈する
- 異なる基本型のポインタ間の変換
int* ip;
char* pc = reinterpret_cast<char*>(ip);
- 基本型ポインタとクラスオブジェクトポインタ間の変換
- アドレス値を整数に変換
reinterpret_castは機械依存性が高く、安全に使用するには型の詳細と変換プロセスを理解する必要がある。
まとめ
- C/C++では型の計算や代入時に暗黙または明示的に型変換が発生する
- C言語では (new_type_name) expression の形式で変換を行う
- C++では static_cast、dynamic_cast、const_cast、reinterpret_cast などのキーワードで変換を行う
- static_cast はコンパイル時に変換され、C言語の強制型変換に似ている
- dynamic_cast は実行時に型チェックを行い、継承関係のある多態構造でのみ使用可能
- const_cast はconst属性を除去するが、設計で回避すべきである
- reinterpret_cast はメモリのビットを再解釈するため、開発者が何をしているのかを明確にする必要がある