VB のオブジェクト操作はポインタが基本

VB では、即値に対する操作とオブジェクトに対する操作を、しっかり区別していないとハマることがある。特に、オブジェクトに対する操作は、ポインタが基本になっていることに注意が必要だ。オブジェクト型 (Object 型だけではなく、特定のクラスを指定した場合も含む) の変数はポインタを格納するものなので、プロシージャに対して ByVal で渡しても、ポインタのコピーが発生するだけで、String のように「速度上げるためには ByRef で」というのはさほど重要じゃない。
以下のコードは、オブジェクトのポインタ (アドレス) を取得する隠し関数 ObjPtr を使って、ByVal と ByRef の違いを調べるサンプル。

Option Explicit

Sub Foo(ByVal o As Class1)
    Debug.Print "foo1: " & ObjPtr(o)
    Set o = New Class1
    Debug.Print "foo2: " & ObjPtr(o)
End Sub

Sub Bar(ByRef o As Class1)
    Debug.Print "bar1: " & ObjPtr(o)
    Set o = New Class1
    Debug.Print "bar2: " & ObjPtr(o)
End Sub

Sub Main()
    Dim o As Class1
    
    Set o = New Class1
    Debug.Print "main1: " & ObjPtr(o)
    Foo o
    Debug.Print "main2: " & ObjPtr(o)
    Bar o
    Debug.Print "main3: " & ObjPtr(o)
End Sub

実行してみると、例えば次のようになる。

main1: 2282584
foo1: 2282584
foo2: 2115336
main2: 2282584
bar1: 2282584
bar2: 2115336
main3: 2115336

どちらの場合もオブジェクトのポインタはそのまま渡されていることと、参照渡しでは呼び出し側の変数が書き換わることがわかる。
オブジェクトを ByVal で渡すのは C/C++ のポインタ渡しに相当し、ByRef で渡すのは C/C++ での「ポインタのポインタ」渡しに相当する、といえるだろう。
……と思っているのだが、上のサンプルで Bar の引数を Class1 から Variant に変えてもコードは実行できて、同じような結果が出る。オブジェクト型と Variant 型の構造が同じってことはないと思うので、VBコンパイラVM がうまくやってくれているのか、僕の理解が間違っているのか、どっちか。