質問

私は 20 年近く C および C++ のコードを書いてきましたが、これらの言語にはまったく理解できていない側面があります。私は明らかに通常のキャストを使用しました。

MyClass *m = (MyClass *)ptr;

とあちこちにあるのですが、他にも2種類のキャストがあるようで、違いが分かりません。次のコード行の違いは何ですか?

MyClass *m = (MyClass *)ptr;
MyClass *m = static_cast<MyClass *>(ptr);
MyClass *m = dynamic_cast<MyClass *>(ptr);
役に立ちましたか?

解決

static_cast

static_cast は、基本的に暗黙的な変換を元に戻したい場合に使用されますが、いくつかの制限と追加があります。 static_cast 実行時チェックは実行されません。これは、特定のタイプのオブジェクトを参照していることが分かっているため、チェックが不要な場合に使用する必要があります。例:

void func(void *data) {
  // Conversion from MyClass* -> void* is implicit
  MyClass *c = static_cast<MyClass*>(data);
  ...
}

int main() {
  MyClass c;
  start_thread(&func, &c)  // func(&c) will be called
      .join();
}

この例では、 MyClass オブジェクトなので、これを確認するための実行時チェックは必要ありません。

ダイナミックキャスト

dynamic_cast オブジェクトの動的タイプがわからない場合に便利です。参照されたオブジェクトに基底クラスとしてキャストされた型が含まれていない場合は、null ポインターを返します (参照にキャストすると、 bad_cast その場合には例外がスローされます)。

if (JumpStm *j = dynamic_cast<JumpStm*>(&stm)) {
  ...
} else if (ExprStm *e = dynamic_cast<ExprStm*>(&stm)) {
  ...
}

使用できません dynamic_cast ダウンキャスト (派生クラスにキャスト) し、引数の型がポリモーフィックではない場合。たとえば、次のコードは無効です。 Base 仮想関数が含まれていません:

struct Base { };
struct Derived : Base { };
int main() {
  Derived d; Base *b = &d;
  dynamic_cast<Derived*>(b); // Invalid
}

「アップキャスト」(基本クラスへのキャスト)は、両方の場合に常に有効です。 static_cast そして dynamic_cast, 「アップキャスト」は暗黙の変換であるため、キャストも必要ありません。

レギュラーキャスト

これらのキャストは C スタイル キャストとも呼ばれます。C スタイルのキャストは、基本的に、C++ キャストの一連のシーケンスを試して、何も考慮せずに機能する最初の C++ キャストを取得することと同じです。 dynamic_cast. 。言うまでもなく、これはすべてを組み合わせたものであるため、はるかに強力です。 const_cast, static_cast そして reinterpret_cast, 、ただし、を使用しないため、安全でもありません。 dynamic_cast.

さらに、C スタイルのキャストを使用すると、これが可能になるだけでなく、プライベート基本クラスに安全にキャストできるようになります。 static_cast シーケンスを使用すると、コンパイル時にエラーが発生します。

簡潔さのため、C スタイルのキャストを好む人もいます。私はこれらを数値キャストにのみ使用し、ユーザー定義型が関係する場合は、より厳密なチェックを提供する適切な 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

キャストの再解釈

C スタイルのキャストがバックグラウンドで行うのと同じ方法でポインター変換を強制するには、代わりに再解釈キャストが使用されます。

int *r = reinterpret_cast<int*>(&c); // forced conversion

このキャストは、あるポインター型から別の互換性のないポインター型への変換など、特定の無関係な型間の変換を処理します。基礎となるビット パターンを変更せずに、データのバイナリ コピーを実行するだけです。このような低レベルの操作の結果はシステム固有のものであるため、移植できないことに注意してください。完全に回避できない場合は、注意して使用する必要があります。

ダイナミックキャスト

これは、オブジェクト ポインターとオブジェクト参照を継承階層内の他のポインターまたは参照型に変換するためにのみ使用されます。これは、ポインタが宛先型の完全なオブジェクトを参照しているかどうかを実行時チェックを実行することにより、指すオブジェクトが変換可能であることを確認する唯一のキャストです。この実行時チェックを可能にするには、オブジェクトが多態性である必要があります。つまり、クラスは少なくとも 1 つの仮想関数を定義または継承する必要があります。これは、コンパイラがそのようなオブジェクトに必要な実行時型情報のみを生成するためです。

動的キャストの例

以下の例では、動的キャストを使用して MyChild ポインターが MyBase ポインターに変換されます。Child オブジェクトには完全な Base オブジェクトが含まれているため、この派生から Base への変換は成功します。

class MyBase 
{ 
  public:
  virtual void test() {}
};
class MyChild : public MyBase {};



int main()
{
  MyChild *child = new MyChild();
  MyBase  *base = dynamic_cast<MyBase*>(child); // ok
}

次の例では、MyBase ポインターを MyChild ポインターに変換しようとします。Base オブジェクトには完全な Child オブジェクトが含まれていないため、このポインタ変換は失敗します。これを示すために、動的キャストは null ポインターを返します。これにより、実行時に変換が成功したかどうかを確認する便利な方法が得られます。

MyBase  *base = new MyBase();
MyChild *child = dynamic_cast<MyChild*>(base);


if (child == 0) 
std::cout << "Null pointer returned";

ポインタの代わりに参照が変換されると、動的キャストは bad_cast 例外をスローして失敗します。これは、try-catch ステートメントを使用して処理する必要があります。

#include <exception>
// …  
try
{ 
  MyChild &child = dynamic_cast<MyChild&>(*base);
}
catch(std::bad_cast &e) 
{ 
  std::cout << e.what(); // bad dynamic_cast
}

動的キャストまたは静的キャスト

動的キャストを使用する利点は、プログラマが実行時に変換が成功したかどうかを確認できることです。欠点は、このチェックの実行に関連してパフォーマンスのオーバーヘッドが発生することです。このため、派生から基底への変換は決して失敗しないため、最初の例では静的キャストを使用することが望ましいと考えられます。

MyBase *base = static_cast<MyBase*>(child); // ok

ただし、2 番目の例では、変換は成功するか失敗する可能性があります。MyBase オブジェクトに MyBase インスタンスが含まれている場合は失敗し、MyChild インスタンスが含まれている場合は成功します。状況によっては、これは実行時まで分からない場合があります。このような場合には、静的キャストよりも動的キャストの方が良い選択となります。

// Succeeds for a MyChild object
MyChild *child = dynamic_cast<MyChild*>(base);

動的キャストではなく静的キャストを使用してベースから派生への変換が実行された場合、変換は失敗しませんでした。不完全なオブジェクトを参照するポインタを返していた可能性があります。このようなポインターを逆参照すると、実行時エラーが発生する可能性があります。

// Allowed, but invalid
MyChild *child = static_cast<MyChild*>(base);

// Incomplete MyChild object dereferenced
(*child);

定数キャスト

これは主に、変数の const 修飾子を追加または削除するために使用されます。

const int myConst = 5;
int *nonConst = const_cast<int*>(&myConst); // removes const

const キャストを使用すると定数の値を変更できますが、これは依然として無効なコードであり、実行時エラーが発生する可能性があります。これは、たとえば定数が読み取り専用メモリのセクションにある場合に発生する可能性があります。

*nonConst = 10; // potential run-time error

Const キャストは、ポインティを変更しない場合でも、非定数ポインター引数を取る関数がある場合に主に使用されます。

void print(int *p) 
{
   std::cout << *p;
}

その後、const キャストを使用して関数に定数変数を渡すことができます。

print(&myConst); // error: cannot convert 
                 // const int* to int*

print(nonConst); // allowed

ソースとその他の説明

記事を見ればいいよ C++ プログラミング/型キャスト.

さまざまなキャスト タイプすべてについての詳しい説明が含まれています。以下は上記のリンクから抜粋したものです。

const_cast

const_cast(expression)const_cast <>()は、変数のconst(ness)(または揮発性)を追加/削除するために使用されます。

static_cast

static_cast(expression)static_cast <>()は、整数型間のキャストに使用されます。'eg' char-> long、int-> shortなど

静的キャストは、関連するタイプへのポインターをキャストするためにも使用されます。たとえば、void*を適切なタイプにキャストします。

ダイナミックキャスト

ダイナミックキャストは、通常、ポインターをキャストする目的で、または相続チェーン(継承階層)の上または下に参照する目的で、実行時にポインターと参照を変換するために使用されます。

動的キャスト(式)

ターゲットタイプはポインターまたは参照タイプでなければならず、式はポインターまたは参照を評価する必要があります。ダイナミックキャストは、式が参照するオブジェクトのタイプがターゲットタイプと互換性があり、ベースクラスに少なくとも1つの仮想メンバー関数がある場合にのみ機能します。そうでない場合、キャストされる式のタイプがポインターである場合、Nullが返されます。参照の動的キャストが失敗した場合、Bad_cast例外がスローされます。失敗しない場合、動的キャストは、式が参照されているオブジェクトにターゲットタイプのポインターまたは参照を返します。

再解釈_キャスト

再解釈キャストは、ある型を別の型にビットごとにキャストするだけです。ポインターまたは積分タイプは、再解釈キャストで他の任意の型にキャストでき、誤用を簡単に可能にします。たとえば、再解釈することで、キャストされていない場合は、文字列ポインターへの整数ポインターをキャストします。

参考までに、Bjarne Stroustrup 氏は、C スタイルのキャストは避けるべきであり、可能であれば static_cast またはdynamic_cast を使用する必要があると述べていると思います。

Barne Stroustrup の C++ スタイル FAQ

そのアドバイスをぜひ受け入れてください。私は C++ の第一人者には程遠いです。

C スタイルのキャストの使用は避けてください。

C スタイルのキャストは const キャストと再解釈キャストが混在しているため、コード内で検索して置換するのは困難です。C++ アプリケーション プログラマは、C スタイルのキャストを避ける必要があります。

C スタイルのキャストは、const_cast、static_cast、reinterpret_cast を混同します。

C++ に C スタイルのキャストがなければよかったのにと思います。C++ キャストは適切に目立ちます (当然のことですが、キャストは通常​​、何か悪いことをしていることを示しており、キャストが実行するさまざまな種類の変換を適切に区別します。また、同様に見える関数を記述することもできます。boost::lexical_cast は、一貫性の観点から非常に優れています。

dynamic_cast 実行時の型チェックがあり、参照とポインターでのみ機能します。 static_cast 実行時の型チェックは提供されません。詳細については、MSDN の記事を参照してください。 static_cast 演算子.

dynamic_cast ポインタ型と参照型のみをサポートします。戻ります NULL 型がポインターの場合はキャストが不可能な場合、または型が参照型の場合は例外をスローする場合。したがって、 dynamic_cast オブジェクトが特定のタイプであるかどうかを確認するために使用できます。 static_cast できません (無効な値が得られるだけです)。

C スタイル (およびその他) キャストは他の回答でカバーされています。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top