web-dev-qa-db-ja.com

Static_cast、dynamic_cast、const_cast、およびreinterpret_castはいつ使用する必要がありますか?

正しい使い方は何ですか?

  • static_cast
  • dynamic_cast
  • const_cast
  • reinterpret_cast
  • Cスタイルキャスト(type)value
  • 関数型キャストtype(value)

どの特定の場合にどのように使用するかをどのように決定しますか?

2265
e.James

static_castは最初に使うべきキャストです。型間の暗黙的な変換(intからfloatへのポインタ、またはvoid*へのポインタなど)などの処理を行い、明示的な変換関数(または暗黙的な変換関数)を呼び出すこともできます。多くの場合、明示的にstatic_castを記述する必要はありませんが、T(something)構文は(T)somethingと同等であり、避けるべきであることに注意することが重要です。 T(something, something_else)は安全ですが、コンストラクタを呼び出すことが保証されています。

static_castは継承階層を介してキャストすることもできます。上向きに(基本クラスに向かって)キャストする場合は不要ですが、下向きにキャストする場合は、virtual継承を介してキャストしない限り使用できます。ただし、チェックは行いません。実際にはオブジェクトの型ではない型に階層をstatic_castすることは、未定義の動作です。


const_castは、変数からconstを削除または追加するために使用できます。他のC++キャストで削除することはできません(reinterpret_castでさえも)。元の変数がconstの場合、以前のconst値を変更することは未定義であることに注意することが重要です。 constで宣言されていないものへの参照からconstを外すためにそれを使用する場合、それは安全です。これは、たとえばconstに基づいてメンバ関数をオーバーロードするときに便利です。メンバ関数のオーバーロードを呼び出すなど、オブジェクトにconstを追加するためにも使用できます。

const_castvolatileでも同様に機能しますが、あまり一般的ではありません。


dynamic_castは多態性を扱うためだけに使われます。多相型へのポインタまたは参照を他のクラス型にキャストすることができます(多相型には、宣言または継承された少なくとも1つの仮想関数があります)。あなたは単に下方にキャストすること以上にそれを使うことができます - あなたは横にキャストすることができますまたはさらに別のチェーンの上にキャストすることができます。 dynamic_castは目的のオブジェクトを探し出し、可能であればそれを返します。それができない場合は、ポインタの場合はnullptrを返すか、参照の場合はstd::bad_castをスローします。

ただし、dynamic_castにはいくつかの制限があります。継承階層内に同じ型のオブジェクトが複数存在している場合(いわゆる 'ひし形')、virtual継承を使用していない場合は機能しません。また、パブリック継承のみを通過することもできます。常にprotectedまたはprivate継承を通過することはできません。ただし、このような形式の継承はめったにないため、これはめったに問題になりません。


reinterpret_castは最も危険なキャストであり、慎重に使用してください。あるポインタから別のポインタに値をキャストしたり、ポインタをintに格納したり、その他のあらゆる厄介なことに使用するなど、あるタイプを別のタイプに直接変換します。 reinterpret_castで得られる唯一の保証は、通常、結果を元の型にキャストバックした場合、まったく同じ値が返されるということです(ただし、中間型がより小さい場合はnot元のタイプ) reinterpret_castでは実行できない変換も多数あります。生のデータストリームを実際のデータに変換したり、整列ポインタの下位ビットにデータを格納するなど、特に変な変換やビット操作に主に使用されます。


C-style castおよびfunction-style castは、それぞれ(type)objectまたはtype(object)を使用したキャストであり、機能的に同等です。これらは、成功した次のうちの最初のものとして定義されます。

  • const_cast
  • static_cast(ただし、アクセス制限は無視されます)
  • static_cast(上記参照)、そしてconst_cast
  • reinterpret_cast
  • reinterpret_cast、次にconst_cast

したがって、場合によっては他のキャストの代わりとして使用できますが、reinterpret_castに解決する機能があるため非常に危険な場合があります。明示的なキャストが必要な場合は、static_castが成功するかどうかを確認してください。 reinterpret_castは失敗します。それでも、もっと長く、より明確な選択肢を考えてください。

Cスタイルのキャストでは、static_castの実行時にアクセス制御も無視されます。つまり、他のキャストでは不可能な操作を実行できます。ただし、これはほとんど問題ではありません。私の考えでは、Cスタイルのキャストを避けるもう1つの理由です。

2330
coppro

継承階層内のポインタ/参照を変換するためにdynamic_castを使います。

通常の型変換にはstatic_castを使用してください。

ビットパターンの低レベルの再解釈にはreinterpret_castを使用してください。細心の注意を払って使用してください。

const_castを捨てるためにconst/volatileを使用してください。 const-incorrect APIを使用しているのでない限り、これを避けてください。

303
Fred Larson

(上記で理論的および概念的な説明がたくさんあります) 

static_cast dynamic_cast const_cast reinterpret_cast を使用したときの 実用例 .

(また、説明を理解するためにこれを参照してください: http://www.cplusplus.com/doc/tutorial/typecasting/

static_cast:

OnEventData(void* pData)

{
  ......

  //  pData is a void* pData, 

  //  EventData is a structure e.g. 
  //  typedef struct _EventData {
  //  std::string id;
  //  std:: string remote_id;
  //  } EventData;

  // On Some Situation a void pointer *pData
  // has been static_casted as 
  // EventData* pointer 

  EventData *evtdata = static_cast<EventData*>(pData);
  .....
}

dynamic_cast:

void DebugLog::OnMessage(Message *msg)
{
    static DebugMsgData *debug;
    static XYZMsgData *xyz;

    if(debug = dynamic_cast<DebugMsgData*>(msg->pdata)){
        // debug message
    }
    else if(xyz = dynamic_cast<XYZMsgData*>(msg->pdata)){
        // xyz message
    }
    else/* if( ... )*/{
        // ...
    }
}

const_cast:

// *Passwd declared as a const

const unsigned char *Passwd


// on some situation it require to remove its constness

const_cast<unsigned char*>(Passwd)

reinterpret_cast:

typedef unsigned short uint16;

// Read Bytes returns that 2 bytes got read. 

bool ByteBuffer::ReadUInt16(uint16& val) {
  return ReadBytes(reinterpret_cast<char*>(&val), 2);
}
170
Sumit Arora

あなたが少しの内部を知っているなら、それは役に立つかもしれません...

static_cast

  • C++コンパイラは、floatからintなどのスケーラ型間の変換方法を既に知っています。それらにはstatic_castを使ってください。
  • コンパイラに型AからBへの変換を依頼すると、static_castBをparamとして渡してAのコンストラクタを呼び出します。あるいは、Aは変換演算子(すなわちA::operator B())を持つことができます。 Bがそのようなコンストラクタを持っていないか、Aが変換演算子を持っていない場合は、コンパイル時エラーが発生します。 
  • AとBが継承階層(またはvoid)にある場合、A*からB*へのキャストは常に成功します。そうしないと、コンパイルエラーが発生します。
  • Gotcha :実ポインタが派生ポインタにキャストされていても、実際のオブジェクトが実際に派生型ではない場合は、don'tエラーを取得してください。 A&からB&まで。
  • Gotcha :DerivedからBaseまたはその逆へのキャストはnewcopy!を作成します。C#/ Javaから来た人々にとって、結果は基本的にDerivedから作成されたオブジェクトの切り捨てです。

dynamic_cast

  • dynamic_castはランタイム型情報を使用してキャストが有効かどうかを判断します。たとえば、ポインタが実際に派生型ではない場合、(Base*)から(Derived*)は失敗する可能性があります。
  • つまり、dynamic_castはstatic_castと比べて非常に高価です。
  • A*からB*の場合、キャストが無効な場合、dynamic_castはnullptrを返します。
  • A&からB&のキャストが無効な場合、dynamic_castはbad_cast例外をスローします。
  • 他のキャストとは異なり、実行時のオーバーヘッドがあります。

const_cast

  • Static_castはconstに対してnon-constを実行できますが、それ以外の方法では実行できません。 const_castには両方の方法があります。
  • これが便利になる1つの例は、キーを変更しないようにするために、その要素のみをconstとして返す、set<T>のようなコンテナを反復処理することです。しかし、あなたの目的がオブジェクトの非キーメンバーを変更することであれば、それは問題ないはずです。あなたはconstnessを削除するためにconst_castを使うことができます。
  • もう1つの例は、T& foo()const T& foo()を実装したい場合です。コードの重複を避けるために、ある関数の値を別の関数から返すためにconst_castを適用することができます。

reinterpret_cast

  • これは基本的にこのメモリ位置にこれらのバイトを取り、与えられたオブジェクトとしてそれを考えることを言います。
  • たとえば、4バイトのfloatを4バイトのintにロードして、floatのビットがどのように見えるかを確認できます。
  • 明らかに、データがそのタイプに対して正しくない場合は、segfaultになる可能性があります。
  • このキャストには実行時のオーバーヘッドはありません。
67
Shital Shah

これ あなたの質問に答えますか?

私はreinterpret_castを使ったことは一度もありませんが、それを必要とするケースに遭遇しても、悪いデザインの匂いではないかと疑問に思います。私が取り組んでいるコードベースではdynamic_castがよく使われています。 static_castとの違いは、dynamic_castが実行時検査を行うことです。これは、(より安全な)またはしない(より多くのオーバーヘッド)必要なものである可能性があります( msdn を参照)。

12
andreas buykx

これまでの他の答えに加えて、これはstatic_castが十分ではないのでreinterpret_castが必要とされる明白でない例です。出力パラメータに異なるクラスのオブジェクトへのポインタを返す(共通の基本クラスを共有しない)関数があるとします。そのような関数の実際の例は CoCreateInstance() です(最後のパラメータ、実際にはvoid**を参照)。この関数から特定のクラスのオブジェクトを要求するとします。そのため、ポインタの型はあらかじめわかっています(これはCOMオブジェクトに対してよく行われます)。この場合、あなたはあなたのポインタへのポインタをvoid**static_castにキャストすることはできません:あなたはreinterpret_cast<void**>(&yourPointer)を必要とします。 

コードでは:

#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
    CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
    //static_cast<void**>(&pNetFwPolicy2) would give a compile error
    reinterpret_cast<void**>(&pNetFwPolicy2) );

ただし、static_castは単純なポインタ(ポインタへのポインタではない)に対して機能するため、次のようにreinterpret_castを回避するように上記のコードを書き換えることができます。

#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
void* tmp = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
    CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
    &tmp );
pNetFwPolicy2 = static_cast<INetFwPolicy2*>(tmp);
12
Serge Rogatch

他の回答ではC++キャスト間のすべての違いについてうまく説明されていますが、Cスタイルのキャスト(Type) varおよびType(var)を使用してはいけない理由を少し説明します。

C++初心者にとっては、CスタイルキャストはC++キャスト(static_cast <>()、dynamic_cast <>()、const_cast <>()、reinterpret_cast <>())のスーパーセット操作であるように見え、誰かがC++キャストより優先します。 。実際にはCスタイルのキャストがスーパーセットであり、書くのがより短いです。

Cスタイルキャストの主な問題は、開発者がキャストを意図していないことです。 Cスタイルのキャストでは、static_cast <>()およびdynamic_cast <>()による通常の安全なキャストからconst_cast <>()のような潜在的に危険なキャストまで、事実上すべての種類のキャストが可能です。整数値をポインタに再解釈することさえ可能な、修正およびreinterpret_cast <>()が可能です。

これがサンプルです。

int a=Rand(); // Random number.

int* pa1=reinterpret_cast<int*>(a); // OK. Here developer clearly expressed he wanted to do this potentially dangerous operation.

int* pa2=static_cast<int*>(a); // Compiler error.
int* pa3=dynamic_cast<int*>(a); // Compiler error.

int* pa4=(int*) a; // OK. C-style cast can do such cast. The question is if it was intentional or developer just did some typo.

*pa4=5; // Program crashes.

C++キャストが言語に追加された主な理由は、開発者が自分の意図を明確にできるようにするためでした - なぜ彼がそのキャストをするつもりなのか。 C++で完全に有効なCスタイルのキャストを使用することで、特にコードを作成しなかった他の開発者にとって、コードが読みにくくなり、エラーが発生しやすくなります。そのため、コードをより読みやすく明示的にするためには、常にCスタイルのキャストよりもC++のキャストを好むべきです。

これはBjarne Stroustrup(C++の作者)の本The C++ Programming Language第4版 - 302ページからの短い引用です。

このCスタイルのキャストは、名前の付いた変換演算子よりもはるかに危険です。これは、大規模プログラムでは表記が難しく、プログラマが意図している変換の種類が明示的ではないためです。

6
Timmy_A

理解するために、以下のコードスニペットを考えてみましょう。

struct Foo{};
struct Bar{};

int main(int argc, char** argv)
{
    Foo* f = new Foo;

    Bar* b1 = f;                              // (1)
    Bar* b2 = static_cast<Bar*>(f);           // (2)
    Bar* b3 = dynamic_cast<Bar*>(f);          // (3)
    Bar* b4 = reinterpret_cast<Bar*>(f);      // (4)
    Bar* b5 = const_cast<Bar*>(f);            // (5)

    return 0;
}

行(4)のみがエラーなしでコンパイルされます。 reinterpret_castのみを使用して、オブジェクトへのポインターを無関係なオブジェクトタイプへのポインターに変換できます。

注目すべきことは次のとおりです。dynamic_castは実行時に失敗しますが、ほとんどのコンパイラでは、キャストされるポインタの構造に仮想関数がないため、コンパイルも失敗します。 dynamic_castは、ポリモーフィッククラスポインターのみで機能します。

C++キャストを使用する場合

  • static_castは、値の変換を行うCスタイルのキャストと同等のものとして、またはクラスからスーパークラスへのポインターを明示的にアップキャストする必要がある場合に使用します。
  • const_castを使用してconst修飾子を削除します。
  • reinterpret_castを使用して、整数型や他のポインター型との間でポインター型の安全でない変換を行います。これは、何をしているかを知っていて、エイリアスの問題を理解している場合にのみ使用してください。
2