コンパイラに次のように伝えます:「0x10000000 にあるClassName
オブジェクトを想像してください。BaseName
データは、0x10000000 に対して、そのオブジェクトのどこから始まるでしょうか?」
複数のベースを持つクラス オブジェクトのメモリ レイアウトを考えてみましょう。
class A: B, C{};
A オブジェクトを構成するメモリ ブロックには、B に属するデータのチャンク、C に属するデータのチャンク、および A に固有のデータがあります。少なくとも 1 つのベースのデータのアドレスを指定することはできません。クラス インスタンス全体のアドレスと同じように、さまざまなメソッドに渡すポインターの数値this
を変える必要があります。マクロは差の値を取得します。
編集: vtable へのポインターは、慣例により、仮想関数を持つクラスの最初のデータ メンバーです。したがって、ベースデータのアドレスを見つけることによって、その vtable ポインターのアドレスが見つかります。
さて、型変換について。通常、ポインターを型キャストする場合、操作は内部的に簡単です。アドレスの数値は、それが指している型に依存しません。データ型の概念そのものが C レベルにのみ存在します。ただし、重要な例外が 1 つあります。多重継承でオブジェクト ポインターをキャストする場合です。先ほど説明したようthis
に、基本クラスのメソッドに渡す必要があるポインターは、派生オブジェクトのものと数値的に異なる場合があります。
したがって、 static_cast と reinterpret_cast の違いは、この違いをうまく捉えています。reinterpret_cast を使用すると、コンパイラに次のように伝えます。これは、型システムの意図的な転覆であり、危険ですが、場合によっては必要になります。この種のキャストは、定義上、些細なことです-あなたがそう言うからです。
「些細な」とは、つまり、ポインターの数値は変化しません。
static_cast は、より高レベルの構造です。この特定のケースでは、オブジェクトとそのベースの間でキャストしています。これは、C++ クラス規則の下では合理的で安全なキャストですが、数値的には自明ではない可能性があります。そのため、マクロは 2 つの異なる型キャストを使用します。static_cast は型システムに違反していません。
要点をまとめると:
reinterpret_cast<ClassName* >(OxlOOOOOOO)
安全でない操作です。偽のオブジェクトへの偽のポインターを返しますが、逆参照しないので問題ありません。
static_cast<BaseName*>(...)
は安全な操作です (危険なポインタ、皮肉を伴います)。重要なポインターの型キャストが発生する部分です。
(DWORD(...)-OxlOOOOOOO)
は純粋な算術です。ここで、危険性が倍増します。ポインターをポインターとして使用するのではなく、整数にキャストし直して、ポインターであったことを忘れてしまいます。
最後の段階は、次のように言い換えることができます。
((char*)(...)-(char*)OxlOOOOOOO)
それがより理にかなっている場合。