の適切な用途は何ですか:
static_castdynamic_castconst_castreinterpret_cast- Cスタイルのキャスト
(type)value - 関数スタイルのキャスト
type(value)
どのように特定のケースでどちらを使用するかをどのように決定しますか
の適切な用途は何ですか:
static_castdynamic_castconst_castreinterpret_cast(type)valuetype(value)どのように特定のケースでどちらを使用するかをどのように決定しますか
回答:
static_cast使用しようとする最初のキャストです。これは、型間の暗黙的な変換(intto floatやへのポインターなど)などを行い、void*明示的な変換関数(または暗黙的な変換関数)を呼び出すこともできます。多くの場合、明示的に述べるstatic_cast必要はありませんが、T(something)構文は同等で(T)somethingあり、避けるべきであることに注意することが重要です(詳細は後で説明します)。T(something, something_else)ただし、A は安全で、コンストラクターの呼び出しが保証されています。
static_cast継承階層をキャストすることもできます。上方向に(基本クラスに向かって)キャストする場合は不要ですが、下方向にキャストする場合は、virtual継承を介してキャストしない限り使用できます。ただし、チェックは行われずstatic_cast、階層を実際にオブジェクトのタイプではないタイプに下げることは未定義の動作です。
const_castconst変数を削除または追加するために使用できます。他のC ++キャストではそれを削除することはできません(であってもreinterpret_cast)。以前のconst値の変更は、元の変数がの場合にのみ未定義になることに注意することが重要ですconst。を使用してconst宣言されていないものへの参照を削除するconst場合は、安全です。これはconst、たとえばに基づいてメンバー関数をオーバーロードする場合に役立ちます。constメンバー関数のオーバーロードを呼び出すなど、オブジェクトに追加するためにも使用できます。
const_castでも同様に機能しますがvolatile、それほど一般的ではありません。
dynamic_castポリモーフィズムの処理にのみ使用されます。ポインタまたはポリモーフィック型への参照を他のクラス型にキャストできます(ポリモーフィック型には、宣言または継承された少なくとも1つの仮想関数があります)。下向きにキャストするだけでなく、横向きにキャストしたり、別のチェーンにキャストしたりすることもできます。dynamic_cast目的のオブジェクトを探し出し、可能であればそれを返します。できないnullptr場合は、ポインターの場合は戻りstd::bad_cast、参照の場合はスローします。
dynamic_castただし、いくつかの制限があります。継承階層に同じタイプのオブジェクトが複数あり(いわゆる「ドレッドダイアモンド」)、virtual継承を使用していない場合は機能しません。また、パブリック継承のみを通過できます。常に通過protectedまたはprivate継承に失敗します。ただし、このような形式の継承はまれであるため、これが問題になることはほとんどありません。
reinterpret_cast最も危険なキャストであり、非常に慎重に使用する必要があります。ある型から別の型に直接値を変換します。たとえば、あるポインタから別のポインタに値をキャストしたり、ポインタをに格納しintたり、あらゆる種類の厄介なものを格納したりします。主に、得られる唯一の保証reinterpret_castは、通常、結果を元の型にキャストすると、まったく同じ値が得られることです(ただし、中間型が元の型よりも小さい場合はそうではありません)。reinterpret_castできない変換もたくさんあります。これは主に、生データストリームを実際のデータに変換したり、整列されたデータへのポインターの下位ビットにデータを格納したりするなど、特に奇妙な変換やビット操作に使用されます。
Cスタイルのキャストと関数スタイルのキャストは、それぞれ(type)objectまたはを使用したキャストtype(object)であり、機能的に同等です。これらは、成功する次の最初のものとして定義されます。
const_caststatic_cast (ただし、アクセス制限は無視されます)static_cast (上記を参照)、次に const_castreinterpret_castreinterpret_cast、その後 const_castしたがって、場合によっては他のキャストの代わりとして使用できますが、にデボルブできるため、非常に危険になる可能性があります。reinterpret_cast明示的なキャストが必要な場合は、static_cast成功またはreinterpret_cast失敗することが確実でない限り、後者が推奨されます。。それでも、より長く、より明示的なオプションを検討してください。
Cスタイルのキャストはstatic_cast、を実行するときにアクセス制御も無視します。つまり、他のキャストではできない操作を実行する機能があります。ただし、これは大部分がクラッジであり、Cスタイルのキャストを回避するもう1つの理由にすぎません。
constできません(さえもreinterpret_cast)」...本当に?どうreinterpret_cast<int *>(reinterpret_cast<uintptr_t>(static_cast<int const *>(0)))ですか?
reinterpret_castは、APIの不透明なデータ型のセットを処理する際によく使用される武器であることに言及する価値があるかもしれません
dynamic_cast継承階層内のポインター/参照の変換に使用します。
static_cast通常の型変換に使用します。
reinterpret_castビットパターンの低レベルの再解釈に使用します。細心の注意を払って使用してください。
const_cast離れてキャストするために使用しますconst/volatile。const-incorrect APIを使用してスタックしない限り、これを回避してください。
(多くの理論的および概念的な説明が上に与えられました)
以下は、いくつかのある具体例私が使用し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);
}
static_cast<char*>(&val)か?
static_cast変換が定義されているタイプ間、継承による可視関係、またはto / fromの間でのみ機能しvoid *ます。他のすべてのために、他のキャストがあります。reinterpret cast任意のchar *型へのアクセスは、任意のオブジェクトの表現の読み取りを許可されます-そして、そのキーワードが役立つ唯一のケースの1つであり、実装/未定義の動作の蔓延するジェネレータではありません。ただし、これは「通常の」変換とは見なされないため、(通常)非常に保守的な変換では許可されていませんstatic_cast。
内部について少し知っていれば役立つかもしれません...
static_cast
static_castそれらに使用します。AにB、static_cast呼び出しBのコンストラクタは、合格Aのparamとして。または、A変換演算子(つまりA::operator B())を含めることもできます。そのBようなコンストラクタがない場合、またはA変換演算子がない場合は、コンパイル時エラーが発生します。A*はB*常に成功します。そうでない場合、コンパイルエラーが発生します。A&のB&。dynamic_cast
(Base*)する(Derived*)ことがあります。A*にB*、キャストが無効である場合、その後はdynamic_castはnullptrを返します。A&にB&キャストが無効である場合、その後はdynamic_castはbad_cast例外がスローされます。const_cast
set<T>、キーを変更しないことを確認するために、constとして要素のみを返すようなコンテナを反復処理することです。ただし、目的がオブジェクトの非キーメンバーを変更することである場合は、問題ありません。const_castを使用してconstnessを削除できます。T& SomeClass::foo()と同様に実装する場合ですconst T& SomeClass::foo() const。コードの重複を回避するために、const_castを適用して、ある関数の戻り値を別の関数から返すことができます。reinterpret_cast
If you cast base pointer to derived pointer but if actual object is not really derived type then you don't get error. You get bad pointer and segfault at runtime..運がよければ、実行時にsegfaultが発生する可能性があるUBを取得します。2.ダイナミックキャストはクロスキャストにも使用できます。3. constキャストは、場合によってはUBになることがあります。を使用mutableすることは、論理的な定数を実装するためのより良い選択かもしれません。
mutable、クロスキャスティングなどに伴う他のすべての複雑化で彼らを圧倒したくありませんでした
これは、あなたの質問に答えますか?
私はを使用reinterpret_castしたことがなく、それを必要とするケースに出くわすことは、悪いデザインの匂いではないかと思います。私が取り組んでいるコードベースでdynamic_castは、よく使われています。との違い static_castは、dynamic_castランタイムチェックが(安全)かそうでない(オーバーヘッドが多い)かを確認することです(msdnを参照)。
reinterpret_castは配列からデータの一部を抽出するために使用しています。たとえばchar*、パックされたバイナリデータでいっぱいの大きなバッファが含まれている場合、移動してさまざまなタイプの個々のプリミティブを取得する必要があります。次のようなもの:template<class ValType> unsigned int readValFromAddress(char* addr, ValType& val) { /*On platforms other than x86(_64) this could do unaligned reads, which could be bad*/ val = (*(reinterpret_cast<ValType*>(addr))); return sizeof(ValType); }
reinterpret_castしたことがありません。その使用方法はそれほど多くありません。
reinterpret_cast、1つの理由で使用されるのを見たことがあります。データベースの「blob」データ型に保存された生のオブジェクトデータを確認しました。その後、データベースからデータを取得すると、reinterpret_castこの生のデータをオブジェクトに変換するために使用されます。
これまでの他の回答に加えて、これ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);
&static_cast<void*>(pNetFwPolicy2)代わりに次のように機能しませんstatic_cast<void**>(&pNetFwPolicy2)か?
他の回答では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修飾子を削除してconst変数を削除できる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 ++の作者)の本、C ++プログラミング言語第4版-ページ302からの短い引用です。
このCスタイルのキャストは、名前付きの変換演算子よりもはるかに危険です。これは、大規模なプログラムでは表記法を見つけるのが難しく、プログラマーが意図する変換の種類が明確でないためです。
理解するために、以下のコードスニペットを考えてみましょう。
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ダウンキャスト/アップキャストのvs dynamic_castvs reinterpret_cast内部ビュー
この回答では、これら3つのメカニズムを具体的なアップキャスト/ダウンキャストの例で比較し、基になるポインター/メモリ/アセンブリがどうなるかを分析して、比較方法を具体的に理解したいと思います。
私はこれがそれらのキャストがどのように異なるかについて良い直感を与えると信じています:
static_cast:実行時に1つのアドレスオフセットを実行し(実行時への影響が少ない)、ダウンキャストが正しいことの安全性チェックは行われません。
dyanamic_cast:はと同様static_castに実行時に同じアドレスオフセットを実行しますが、RTTIを使用してダウンキャストが正しいかどうかのコストのかかる安全性チェックも行います。
この安全性チェックnullptrでは、無効なダウンキャストを示す戻り値をチェックすることにより、実行時に基本クラスポインターが特定の型であるかどうかを照会できます。
したがって、コードでそれを確認できずnullptr、有効な非中止アクションを実行できない場合はstatic_cast、動的キャストの代わりに使用する必要があります。
中止がコードで実行できる唯一のアクションである場合はdynamic_cast、デバッグビルド(-NDEBUG)を有効にし、static_castそれ以外の場合、たとえばここで行ったように使用して、高速実行を遅くしないようにすることができます。
reinterpret_cast:実行時には何もせず、アドレスオフセットもしません。ポインターは、正しい型を正確に指す必要があります。基本クラスでさえ機能しません。生のバイトストリームが関係しない限り、通常はこれを望まないでしょう。
次のコード例を検討してください。
main.cpp
#include <iostream>
struct B1 {
B1(int int_in_b1) : int_in_b1(int_in_b1) {}
virtual ~B1() {}
void f0() {}
virtual int f1() { return 1; }
int int_in_b1;
};
struct B2 {
B2(int int_in_b2) : int_in_b2(int_in_b2) {}
virtual ~B2() {}
virtual int f2() { return 2; }
int int_in_b2;
};
struct D : public B1, public B2 {
D(int int_in_b1, int int_in_b2, int int_in_d)
: B1(int_in_b1), B2(int_in_b2), int_in_d(int_in_d) {}
void d() {}
int f2() { return 3; }
int int_in_d;
};
int main() {
B2 *b2s[2];
B2 b2{11};
D *dp;
D d{1, 2, 3};
// The memory layout must support the virtual method call use case.
b2s[0] = &b2;
// An upcast is an implicit static_cast<>().
b2s[1] = &d;
std::cout << "&d " << &d << std::endl;
std::cout << "b2s[0] " << b2s[0] << std::endl;
std::cout << "b2s[1] " << b2s[1] << std::endl;
std::cout << "b2s[0]->f2() " << b2s[0]->f2() << std::endl;
std::cout << "b2s[1]->f2() " << b2s[1]->f2() << std::endl;
// Now for some downcasts.
// Cannot be done implicitly
// error: invalid conversion from ‘B2*’ to ‘D*’ [-fpermissive]
// dp = (b2s[0]);
// Undefined behaviour to an unrelated memory address because this is a B2, not D.
dp = static_cast<D*>(b2s[0]);
std::cout << "static_cast<D*>(b2s[0]) " << dp << std::endl;
std::cout << "static_cast<D*>(b2s[0])->int_in_d " << dp->int_in_d << std::endl;
// OK
dp = static_cast<D*>(b2s[1]);
std::cout << "static_cast<D*>(b2s[1]) " << dp << std::endl;
std::cout << "static_cast<D*>(b2s[1])->int_in_d " << dp->int_in_d << std::endl;
// Segfault because dp is nullptr.
dp = dynamic_cast<D*>(b2s[0]);
std::cout << "dynamic_cast<D*>(b2s[0]) " << dp << std::endl;
//std::cout << "dynamic_cast<D*>(b2s[0])->int_in_d " << dp->int_in_d << std::endl;
// OK
dp = dynamic_cast<D*>(b2s[1]);
std::cout << "dynamic_cast<D*>(b2s[1]) " << dp << std::endl;
std::cout << "dynamic_cast<D*>(b2s[1])->int_in_d " << dp->int_in_d << std::endl;
// Undefined behaviour to an unrelated memory address because this
// did not calculate the offset to get from B2* to D*.
dp = reinterpret_cast<D*>(b2s[1]);
std::cout << "reinterpret_cast<D*>(b2s[1]) " << dp << std::endl;
std::cout << "reinterpret_cast<D*>(b2s[1])->int_in_d " << dp->int_in_d << std::endl;
}
コンパイル、実行、逆アセンブル:
g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
setarch `uname -m` -R ./main.out
gdb -batch -ex "disassemble/rs main" main.out
どこがsetarchされて無効にASLRを使用し、それが簡単にランを比較することにします。
可能な出力:
&d 0x7fffffffc930
b2s[0] 0x7fffffffc920
b2s[1] 0x7fffffffc940
b2s[0]->f2() 2
b2s[1]->f2() 3
static_cast<D*>(b2s[0]) 0x7fffffffc910
static_cast<D*>(b2s[0])->int_in_d 1
static_cast<D*>(b2s[1]) 0x7fffffffc930
static_cast<D*>(b2s[1])->int_in_d 3
dynamic_cast<D*>(b2s[0]) 0
dynamic_cast<D*>(b2s[1]) 0x7fffffffc930
dynamic_cast<D*>(b2s[1])->int_in_d 3
reinterpret_cast<D*>(b2s[1]) 0x7fffffffc940
reinterpret_cast<D*>(b2s[1])->int_in_d 32767
今、https://en.wikipedia.org/wiki/Virtual_method_tableで言及されているように、仮想メソッド呼び出しを効率的にサポートするために、のメモリデータ構造はD次のようにする必要があります。
B1:
+0: pointer to virtual method table of B1
+4: value of int_in_b1
B2:
+0: pointer to virtual method table of B2
+4: value of int_in_b2
D:
+0: pointer to virtual method table of D (for B1)
+4: value of int_in_b1
+8: pointer to virtual method table of D (for B2)
+12: value of int_in_b2
+16: value of int_in_d
重要な事実は、のメモリデータ構造が、内部および内部とD互換性のあるメモリ構造を内部に含んでいることです。B1B2
したがって、重要な結論に到達します。
アップキャストまたはダウンキャストは、コンパイル時に既知の値だけポインタ値をシフトする必要がある
このようDに、基本型配列に渡されると、型キャストは実際にそのオフセットを計算しB2、メモリ内で有効なように見えるものを指します。
b2s[1] = &d;
これにはのD代わりにvtableがあるためB2、すべての仮想呼び出しは透過的に機能します。
これで、最終的に型キャストと具体例の分析に戻ることができます。
stdout出力から、次のことがわかります。
&d 0x7fffffffc930
b2s[1] 0x7fffffffc940
したがって、static_castそこで行われる暗黙的にD、0x7fffffffc930にある完全なデータ構造から0x7fffffffc940にあるB2類似のデータ構造までのオフセットを正しく計算しました。また、0x7fffffffc930と0x7fffffffc940の間にあるのは、おそらくB1データとvtableであると推測しています。
次に、ダウンキャストセクションで、無効なものがどのように失敗するか、およびその理由を簡単に理解できるようになりました。
static_cast<D*>(b2s[0]) 0x7fffffffc910:コンパイラーは、コンパイル時にバイトで0x10を増やし、a B2からコンテナーに移動しようとしましたD
しかしb2s[0]、Dではなかったため、未定義のメモリ領域をポイントします。
分解は次のとおりです。
49 dp = static_cast<D*>(b2s[0]);
0x0000000000000fc8 <+414>: 48 8b 45 d0 mov -0x30(%rbp),%rax
0x0000000000000fcc <+418>: 48 85 c0 test %rax,%rax
0x0000000000000fcf <+421>: 74 0a je 0xfdb <main()+433>
0x0000000000000fd1 <+423>: 48 8b 45 d0 mov -0x30(%rbp),%rax
0x0000000000000fd5 <+427>: 48 83 e8 10 sub $0x10,%rax
0x0000000000000fd9 <+431>: eb 05 jmp 0xfe0 <main()+438>
0x0000000000000fdb <+433>: b8 00 00 00 00 mov $0x0,%eax
0x0000000000000fe0 <+438>: 48 89 45 98 mov %rax,-0x68(%rbp)
したがって、GCCは次のことを行います。
Dして、存在しないに到達しますdynamic_cast<D*>(b2s[0]) 0:C ++は、キャストが無効であることを実際に検出して返しましたnullptr。
コンパイル時にこれを行う方法はありません。逆アセンブリから確認します。
59 dp = dynamic_cast<D*>(b2s[0]);
0x00000000000010ec <+706>: 48 8b 45 d0 mov -0x30(%rbp),%rax
0x00000000000010f0 <+710>: 48 85 c0 test %rax,%rax
0x00000000000010f3 <+713>: 74 1d je 0x1112 <main()+744>
0x00000000000010f5 <+715>: b9 10 00 00 00 mov $0x10,%ecx
0x00000000000010fa <+720>: 48 8d 15 f7 0b 20 00 lea 0x200bf7(%rip),%rdx # 0x201cf8 <_ZTI1D>
0x0000000000001101 <+727>: 48 8d 35 28 0c 20 00 lea 0x200c28(%rip),%rsi # 0x201d30 <_ZTI2B2>
0x0000000000001108 <+734>: 48 89 c7 mov %rax,%rdi
0x000000000000110b <+737>: e8 c0 fb ff ff callq 0xcd0 <__dynamic_cast@plt>
0x0000000000001110 <+742>: eb 05 jmp 0x1117 <main()+749>
0x0000000000001112 <+744>: b8 00 00 00 00 mov $0x0,%eax
0x0000000000001117 <+749>: 48 89 45 98 mov %rax,-0x68(%rbp)
最初にNULLチェックがあり、einputがNULLの場合はNULLを返します。
それ以外の場合は、RDX、RSI、RDIにいくつかの引数を設定し、を呼び出します__dynamic_cast。
現在、これをさらに分析する忍耐力はありませんが、他の人が言ったように、これが機能する唯一の方法は__dynamic_cast、クラス階層を表す追加のRTTIインメモリデータ構造にアクセスすることです。
したがってB2、そのテーブルのエントリから開始し、次にこのクラス階層をたどって、Dからの型キャストのvtableを見つける必要がありb2s[0]ます。
これが、キャストの再解釈が高価になる可能性がある理由です!以下は、複雑なプロジェクトでa dynamic_castをa static_castに変換する1つのライナーパッチがランタイムを33%削減した例です。。
reinterpret_cast<D*>(b2s[1]) 0x7fffffffc940これは盲目的に私たちを信じているだけです:Datアドレスb2s[1]があり、コンパイラーはオフセット計算を行いません。
しかし、これは間違っています。Dは実際には0x7fffffffc930にあるため、0x7fffffffc940にあるのはD内のB2に似た構造です。だからゴミがアクセスされます。
これ-O0は、値を移動するだけの恐ろしいアセンブリから確認できます。
70 dp = reinterpret_cast<D*>(b2s[1]);
0x00000000000011fa <+976>: 48 8b 45 d8 mov -0x28(%rbp),%rax
0x00000000000011fe <+980>: 48 89 45 98 mov %rax,-0x68(%rbp)関連する質問:
Ubuntu 18.04 amd64、GCC 7.4.0でテスト済み。