ホームに戻る
出典 :
関連 :
目次 :
キャストとは
プログラミングにおける型変換のうち、プログラマが明示的に行うものを指す。
コンパイラが行う暗黙的な型変換もキャストと呼ぶことがあるが、ここでは明示的な型変換のみを取り扱う。
アップキャストとダウンキャスト
クラスの継承を行った場合、基底クラスのポインタは、そのクラスから派生したすべてのクラスのインスタンスを指すことができる。
この、派生クラスから基底クラスのキャストは「アップキャスト」と呼ばれ、明示的なキャストを必要としない。
Derived* dp = new Derived();
Base* bp = dp; // 派生クラスから基底クラスへのアップキャスト
アップキャストとは逆に、基底クラスのインスタンスを派生クラスにキャストすることを「ダウンキャスト」と呼ぶ。
ダウンキャストは、存在しないメンバへのアクセスを許容する危険性があるため、特段の事情がある場合を除き行ってはならない。
(仮想関数を用いるなど、ダウンキャストに頼らない設計を行うべきである。)
C準拠のキャスト
() 演算子によるキャスト
「式」の型を、括弧で指定されたものに変換する。最も基本的なキャスト。
int si = 123;
short ss = (short)si;
上記の例では、int 型変数 si の値を short 型に変換したものを変数 ss に代入している。
ここで明示的なキャスト( (short) )を行わない場合、コンパイラは代入時に int ⇒ short の変換を試みるが、
通常ならば short は int よりもサイズが小さく、情報が欠損する(値が変化する)可能性があるため、コンパイラは警告を発する。
キャストを行うことで、それが意図した型変換であることをコンパイラに伝えることができるが、警告を抑制することはできても情報の欠損を防ぐものではない。
このため、変換前後の値はプログラマが責任を持って管理する必要がある。
() キャストの制約
() 演算子を用いたキャストには、以下のような制約がある。
- 任意の型から void 型への変換が可能
- スカラ型(数値型およびポインタ型)は任意のスカラ型に変換が可能
但し浮動小数点数型( float / double )とポインタ型は相互に変換不可
void main()
{
char c = 0; //< char 型変数 c
struct { int mi; char mc; } st = {0}; //< 構造体型変数 st
// 数値型 ⇒ 数値型
int i = (int)c; //< char ⇒ int
long l = (long)i; //< int ⇒ long
double d = (double)l; //< long ⇒ double
long long ll = (long long)d; //< double ⇒ long long
i = (int)ll; //< long long ⇒ int
// 数値型 ⇔ ポインタ型 (警告が出る可能性あり)
void* vp = (void*)i; //< int ⇒ void*
c = (char)vp; //< void* ⇒ char
// 構造体型 ⇒ void 型
(void)st;
// 以下はエラーとなる(規約違反)
// float f = (float)vp; //< void* ⇒ float
// vp = (void*)f; //< float ⇒ void*
// ll = (long long)st; //< 構造体型 ⇒ 数値型
}
関数形式キャスト
型名(式)
の形式で行うもので、関数呼び出しのように見えることから「関数形式キャスト」と呼ばれる。
() 演算子によるキャストと機能的に同等。
#include <iostream>
void main()
{
std::cout << int(12.3) << std::endl;
}
実行結果
12
静的キャスト (static_cast)
型変換をコンパイル時に行う。数値型同士やポインタ型同士といった、型がコンパイル時に判別している、比較的安全な型変換に用いる。
C++における基本的なキャスト。
double p = 3.14;
int n = static_cast<int>(p);
動的キャスト (dynamic_cast)
型変換を実行時に行う。型がコンパイル時に判別しない場合に適している。
ダウンキャストを行う際には static_cast ではなくこちらを用いることが推奨されている。
(先述の通り、そもそもダウンキャスト自体が推奨されない手法ではある。)
dynamic_cast は変換前後の型の継承関係をチェックし、安全なキャストであると判断した場合にのみキャストが成功する。
このとき、ポインタ型への変換が失敗すると式の評価値は NULL となり、参照型への変換が失敗すると std::bad_cast 例外が投げられる。
(dynamic_cast はランタイム型情報(RTTI)を使用するため、開発環境での設定が必要となる。)
例 : 正常なダウンキャスト
#include <iostream>
// 基底クラス定義
class Base
{
public:
virtual ~Base() { }
};
// 派生クラス定義
class Derived : public Base
{
};
void main()
{
Base* bp = new Derived();
// dynamic_cast によるダウンキャスト
// (失敗時は NULL)
Derived* dp = dynamic_cast<Derived*>(bp);
dp ? std::cout << "dynamic_cast succeeded" << std::endl :
std::cout << "dynamic_cast failed" << std::endl;
delete bp;
}
実行結果
dynamic_cast succeeded
この例では、ダウンキャストに dynamic_cast を用いている。
変数 bp は元々が Derived のインスタンスを指しているため、ダウンキャストで Derived に戻す際に問題は発生しない。
例 : 不正なダウンキャスト(出自の異なる型へのキャスト)
void main()
{
Base* bp = new Base();
Derived* dp = dynamic_cast<Derived*>(bp);
dp ? std::cout << "dynamic_cast succeeded" << std::endl :
std::cout << "dynamic_cast failed" << std::endl;
delete bp;
}
実行結果
dynamic_cast failed
この例では、変数 bp は Base のインスタンスを指しており、出自の異なる Derived へのダウンキャストは失敗する。
例 : 不正なダウンキャスト(参照型へのキャスト)
void main()
{
Base* bp = new Base();
try
{
Derived& dp = dynamic_cast<Derived&>(*bp);
std::cout << "dynamic_cast succeeded" << std::endl :
}
catch ( std::bad_cast& )
{
std::cout << "dynamic_cast failed" << std::endl;
}
}
この例では参照型へのキャストを行っており、失敗時に std::bad_cast 例外が投げられる。
const キャスト (const_cast)
const や volatile の指定を外す際に使用する。
const int* p;
int* q = const_cast<int*>(p); //< const int* から int* への変換
再解釈キャスト (reinterpret_cast)
元のデータに一切変更を加えず、そのまま別の型として解釈する。
static_cast であれば、int 型の 10 を double 型に変換した場合、式の値は 10.0 となるが、
この (int)10 と (double)10.0 はビット表現が異なっており、キャストに伴ってビット表現が書き換えられる。
reinterpret_cast では値を一切操作せず、そのまま(強引に)解釈を行う。
(即ち、(int)10 を reinterpret_cast<double> で変換した結果は 10.0 にならない。)