C言語での型変換
C言語では、代入演算子の両辺の型が異なる場合や、関数の引数と実引数の型が一致しない場合、または戻り値の型と受け取る変数の型が異なる場合には型変換が必要です。
C言語における型変換は主に2つの種類があります:暗黙的な型変換と明示的な型変換です。
- 暗黙的な型変換:コンパイラが自動的に行います。
- 明示的な型変換:ユーザーが指定する必要があります。
void example1() {
int num = 1;
// 暗黙的な型変換
double dbl = num;
printf("%d, %.2f\n", num, dbl);
int* ptr = #
// 明示的な型変換
int addr = (int)ptr;
printf("%x, %d\n", ptr, addr);
}
強制型変換の必要性
Cスタイルの型変換はシンプルですが、いくつかの欠点があります:
- 暗黙的な型変換は時として問題を引き起こすことがあります(例えばデータ精度の損失)。
- 明示的な型変換は全ての状況を混ざり合わせているため、コードが読みづらいことがあります。
これらの理由から、C++では独自の型変換スタイルを導入しました。C++はC言語との互換性を維持するために、C言語の型変換スタイルも使用できます。
C++の強制型変換
C++では、型変換の可視性を強化するために4つの名前付きの強制型変換演算子を導入しました。
- static_cast
- reinterpret_cast
- const_cast
- dynamic_cast
1. static_cast
static_castは非多態型の変換(静的キャスト)に使用され、コンパイラが暗黙的に実行する任意の型変換を表現できますが、関連性のない型間の変換には使用できません。
int main() {
double d = 12.34;
int a = static_cast<int>(d);
std::cout << a << std::endl;
return 0;
}
2. reinterpret_cast
reinterpret_castは、操作数のビットパターンを低レベルで再解釈します。異なる型間の変換に使用されます。
int main() {
double d = 12.34;
int a = static_cast<int>(d);
std::cout << a << std::endl;
// ここではstatic_castを使用するとエラーになるため、reinterpret_castを使用する
// int* p = static_cast(a);
int* p = reinterpret_cast(a);
return 0;
}
3. const_cast
const_castは主に変数のconst属性を削除し、代入を可能にするために使用されます。
void example2() {
const int a = 2;
int* p = const_cast(&a);
*p = 3;
std::cout << a << std::endl;
}
4. dynamic_cast
dynamic_castは親クラスのオブジェクトのポインタ/参照を子クラスのオブジェクトのポインタ/参照に変換する際に使用されます(動的キャスト)。
- アップキャスト:子クラスのポインタ/参照 → 親クラスのポインタ/参照(不要な変換、代入規則に基づいて互換性がある)
- ダウンキャスト:親クラスのポインタ/参照 → 子クラスのポインタ/参照(dynamic_castによる変換は安全)
注意点:
- dynamic_castは親クラスに仮想関数が含まれている場合のみ使用できます。
- dynamic_castは最初に変換が可能かどうかをチェックし、可能であれば変換を実行し、不可能であればnullptrを返します。
- 子クラスは親クラスに変換できますが、親クラスは子クラスに変換することはできません。
- ポインタ/参照は親クラスや子クラスのどちらにもポイントできます。
class Base {
public:
virtual void func() {}
};
class Derived : public Base {};
void func(Base* pb) {
// dynamic_castは最初に変換が可能かどうかをチェックし、可能であれば変換を実行し、不可能であればnullptrを返します
Derived* pd1 = static_cast(pb);
Derived* pd2 = dynamic_cast(pb);
std::cout << "pd1: " << pd1 << std::endl;
std::cout << "pd2: " << pd2 << std::endl;
}
int main() {
Base b;
Derived d;
func(&b);
func(&d);
return 0;
}
強制型変換の使用は避けることを強く推奨します。
強制型変換は通常の型チェックを無効にします。強制型変換を使用する前に、他の方法で同じ目的を達成できないか検討すべきです。強制型変換が不可避な場合は、その影響範囲を最小限に抑えるように努力すべきです。
RTTI
RTTIは「ランタイム型識別」の略称で、C++は以下のような方法でRTTIをサポートしています。
- typeid演算子
- dynamic_cast演算子
- decltype