static_cast
関数はCスタイルまたは単純な関数スタイルのキャストよりも優先されるべきだと聞きました。これは本当ですか?なんで?
9 に答える
static_cast<>()
主な理由は、従来の C のキャストでは、 、reinterpret_cast<>()
、const_cast<>()
、および と呼ばれるものを区別しないためですdynamic_cast<>()
。この4つは全く別物です。
Astatic_cast<>()
は通常安全です。言語に有効な変換があるか、それを可能にする適切なコンストラクターがあります。少し危険なのは、継承されたクラスにキャストするときだけです。言語の外部の手段 (オブジェクト内のフラグなど) によって、オブジェクトが実際に主張する子孫であることを確認する必要があります。Adynamic_cast<>()
は、結果がチェックされる (ポインター) か、考えられる例外が考慮される (参照) 限り安全です。
一方、 A reinterpret_cast<>()
(または a const_cast<>()
) は常に危険です。コンパイラーに次のように伝えますfoo
。
最初の問題は、大規模で分散したコードの断片を見て、すべての規則を知らなければ、C スタイルのキャストでどれが発生するかを判断するのがほとんど不可能だということです。
これらを仮定しましょう:
class CDerivedClass : public CMyBase {...};
class CMyOtherStuff {...} ;
CMyBase *pSomething; // filled somewhere
現在、これら 2 つは同じ方法でコンパイルされています。
CDerivedClass *pMyObject;
pMyObject = static_cast<CDerivedClass*>(pSomething); // Safe; as long as we checked
pMyObject = (CDerivedClass*)(pSomething); // Same as static_cast<>
// Safe; as long as we checked
// but harder to read
ただし、このほぼ同じコードを見てみましょう。
CMyOtherStuff *pOther;
pOther = static_cast<CMyOtherStuff*>(pSomething); // Compiler error: Can't convert
pOther = (CMyOtherStuff*)(pSomething); // No compiler error.
// Same as reinterpret_cast<>
// and it's wrong!!!
ご覧のとおり、関連するすべてのクラスについてよく知らなければ、2 つの状況を簡単に区別する方法はありません。
2 番目の問題は、C スタイルのキャストを見つけるのが難しすぎることです。複雑な式では、C スタイルのキャストを確認するのが非常に難しい場合があります。本格的な C++ コンパイラ フロントエンドなしでは、C スタイルのキャストを検索する必要がある自動化ツール (検索ツールなど) を作成することは事実上不可能です。一方で、「static_cast<」や「reinterpret_cast<」を検索するのは簡単です。
pOther = reinterpret_cast<CMyOtherStuff*>(pSomething);
// No compiler error.
// but the presence of a reinterpret_cast<> is
// like a Siren with Red Flashing Lights in your code.
// The mere typing of it should cause you to feel VERY uncomfortable.
つまり、C スタイルのキャストはより危険であるだけでなく、それらが正しいことを確認するためにそれらすべてを見つけるのが非常に難しいということです。
実用的なヒント: プロジェクトを整理する予定がある場合は、ソース コード内の static_cast キーワードを簡単に検索できます。
要するに:
static_cast<>()
コンパイル時のチェック機能を提供しますが、C スタイルのキャストにはありません。static_cast<>()
C++ ソース コード内のどこにでも簡単に見つけることができます。対照的に、C_Style キャストは見つけにくいです。- C++ キャストを使用すると、意図がはるかによく伝わります。
詳細説明:
静的キャストは、互換性のある型間の変換を実行します。C スタイルのキャストに似ていますが、より制限があります。たとえば、C スタイルのキャストでは、整数ポインターが char を指すことができます。
char c = 10; // 1 byte int *p = (int*)&c; // 4 bytes
これにより、割り当てられたメモリの 1 バイトを指す 4 バイトのポインタが生成されるため、このポインタに書き込むと、実行時エラーが発生するか、隣接するメモリが上書きされます。
*p = 5; // run-time error: stack corruption
C スタイルのキャストとは対照的に、静的キャストを使用すると、コンパイラはポインターとポインティング先のデータ型に互換性があることを確認できます。これにより、プログラマーはコンパイル中にこの不適切なポインターの割り当てを見つけることができます。
int *q = static_cast<int*>(&c); // compile-time error
詳細を読む:
static_cast<> と C スタイルのキャストの違い
と、
通常のキャストと static_cast と dynamic_castの違いは何ですか
C スタイルのキャストを使用するとさまざまなことが起こるため、問題は wither static_cast または C スタイルのキャストを使用するだけではありません。C++ キャスト演算子は、これらの操作をより明示的にすることを目的としています。
表面的には、 static_cast と C スタイルのキャストは同じものに見えます。たとえば、ある値を別の値にキャストする場合です。
int i;
double d = (double)i; //C-style cast
double d2 = static_cast<double>( i ); //C++ cast
どちらも整数値を double にキャストします。ただし、ポインターを操作する場合は、さらに複雑になります。いくつかの例:
class A {};
class B : public A {};
A* a = new B;
B* b = (B*)a; //(1) what is this supposed to do?
char* c = (char*)new int( 5 ); //(2) that weird?
char* c1 = static_cast<char*>( new int( 5 ) ); //(3) compile time error
この例 (1) では、A が指すオブジェクトは実際には B のインスタンスであるため、おそらく問題ありません。(2) たぶん完全に合法 (整数の 1 バイトだけを見たい) かもしれませんが、(3) のようにエラーが発生する場合は間違いである可能性もあります。C++ キャスト オペレーターは、可能な場合にコンパイル時または実行時のエラーを提供することにより、コードでこれらの問題を明らかにすることを目的としています。
したがって、厳密な「値のキャスト」には static_cast を使用できます。ポインターのランタイム ポリモーフィック キャストが必要な場合は、dynamic_cast を使用します。本当にタイプを忘れたい場合は、reintrepret_cast を使用できます。const をウィンドウの外に放り出すには、const_cast があります。
コードをより明示的にするだけで、自分が何をしていたかを知っているように見えます。
static_cast
const_cast
は、誤ってまたはができないことを意味しますreinterpret_cast
。これは良いことです。
- grep または同様のツールを使用して、コード内でキャストを簡単に見つけることができます。
- 実行しているキャストの種類を明示し、それを強制するためにコンパイラの助けを借ります。const 性だけをキャストしたい場合は、const_cast を使用できます。これにより、他のタイプの変換を行うことができなくなります。
- キャストは本質的に醜いものです。プログラマーは、コンパイラーが通常コードを処理する方法を覆すことになります。あなたはコンパイラに「私はあなたよりよく知っている」と言っています。その場合、キャストを実行するのは適度に面倒なことであり、問題の原因となる可能性が高いため、コード内で目立つようにすることは理にかなっています。
効果的な C++ の紹介を参照してください
それは、どの程度の型安全性を課したいかについてです。
書くとき(bar) foo
(これは、型変換演算子を提供していない場合と同等reinterpret_cast<bar> foo
です) は、型の安全性を無視し、指示どおりに行うようにコンパイラに指示していることになります。
あなたが書いているときstatic_cast<bar> foo
は、少なくとも型変換が意味をなすことを確認し、整数型の場合は何らかの変換コードを挿入するようにコンパイラに要求しています。
編集 2014-02-26
私はこの回答を5年以上前に書きましたが、間違っていました。(コメントを参照してください。)しかし、それでも賛成票を獲得しています!
C スタイルのキャストは、コード ブロック内で見逃されがちです。C++ スタイルのキャストは、優れた方法であるだけではありません。それらははるかに高い柔軟性を提供します。
reinterpret_cast を使用すると、整数型からポインター型への変換が可能になりますが、誤用すると安全でなくなる可能性があります。
static_cast は、数値型の適切な変換を提供します。たとえば、列挙型から int へ、または int から float へ、または型に自信のある任意のデータ型への変換です。ランタイム チェックは実行されません。
一方、dynamic_cast はこれらのチェックを実行し、あいまいな割り当てまたは変換にフラグを立てます。ポインターと参照でのみ機能し、オーバーヘッドが発生します。
他にもいくつかありますが、これらはあなたが遭遇する主なものです。
static_castは、クラスへのポインターの操作の他に、クラスで明示的に定義された変換を実行したり、基本タイプ間の標準変換を実行したりするためにも使用できます。
double d = 3.14159265;
int i = static_cast<int>(d);