(for Internet Explorer)
CaseA
WScript.echo  "Returned"

Set object = CaseB()
WScript.echo  "Returned"
object = Empty

Set object = CaseC()
WScript.echo  "Returned"
object = Empty

Set object = CaseD()
WScript.echo  "Returned"
object = Empty

CaseE
WScript.echo  "Returned"

CaseF
WScript.echo  "Returned"

WScript.echo  "End of Main"
WScript.echo  "もし、ここ以降でデストラクターが動くときは、"+_
    "プログラム終了時にすべてのオブジェクトを削除する"+_
    "ことによるデストラクター呼び出しです。"


Sub  CaseA()  '// 相互参照しているオブジェクトの削除
    WScript.echo  vbCRLF +"CaseA:"
    Set a_handle = new RootHandleClass       : a_handle.p.Name = "A1"
    Set b_handle = a_handle.p.CreateMember() : b_handle.p.Name = "A2"
    Set a_handle.p.Reference = b_handle.p
    Set b_handle.p.Reference = a_handle.p
End Sub

Function  CaseB()  '// ハンドルを返すとき
    WScript.echo  vbCRLF +"CaseB:"
    Set a_handle = new RootHandleClass       : a_handle.p.Name = "B1"
    Set b_handle = a_handle.p.CreateMember() : b_handle.p.Name = "B2"
    Set a_handle.p.Reference = b_handle.p
    Set b_handle.p.Reference = a_handle.p
    Set CaseB = b_handle
End Function

Function  CaseC()  '// LefeGroup から外れるとき
    WScript.echo  vbCRLF +"CaseC:"
    Set a_handle = new RootHandleClass       : a_handle.p.Name = "C1"
    Set b_handle = a_handle.p.CreateMember() : b_handle.p.Name = "C2"
    Set c_handle = b_handle.p.CreateMember() : c_handle.p.Name = "C3"
    a_handle.p.LifeGroup.Group.Remove  c_handle.p
    Set a_handle.p.Reference = b_handle.p
    Set b_handle.p.Reference = a_handle.p
    Set CaseC = c_handle
End Function

Function  CaseD()  '// ハンドルを複製するとき
    WScript.echo  vbCRLF +"CaseD:"
    Set a_handle = new RootHandleClass       : a_handle.p.Name = "D1"
    Set b_handle = a_handle.p.CreateMember() : b_handle.p.Name = "D2"
    Set a_handle.p.Reference = b_handle.p
    Set b_handle.p.Reference = a_handle.p
    Set CaseD = b_handle.p.GetReference()
End Function

Sub  CaseE()  '// 所属オブジェクトの参照カウンターが 0 から 1 に戻るとき
    WScript.echo  vbCRLF +"CaseE:"
    Set a_handle = new RootHandleClass       : a_handle.p.Name = "E1"
    Set b_handle = a_handle.p.CreateMember() : b_handle.p.Name = "E2"
    Set a_handle.p.Reference = b_handle.p
    Set b_handle.p.Reference = a_handle.p
    b_handle = Empty
    Set b_handle = a_handle.p.GetReference()
End Sub

Sub  CaseF()  '// LefeGroup を再利用するとき
    WScript.echo  vbCRLF +"CaseF:"
    For count = 1  To 2
        Set a_handle = new RootHandleClass       : a_handle.p.Name = "F1"
        Set b_handle = a_handle.p.CreateMember() : b_handle.p.Name = "F2"
        Set a_handle.p.Reference = b_handle.p
        Set b_handle.p.Reference = a_handle.p

        a_handle = Empty
        b_handle = Empty
    Next
End Sub


Class  MemberClass
    Public  Name
    Public  Reference

    Public  LifeGroup  '// Don't write except for LifeGroupClass ■追加

    Private Sub  Class_Terminate()
        WScript.echo  Me.Name +": MemberClass::Class_Terminate"
    End Sub

    Public Sub  DestroyReferences()  '// ■追加
        Me.Reference = Empty
    End Sub

    Public Function  CreateMember()
        Set CreateMember = Me.LifeGroup.Group.Add( new MemberClass )
    End Function

    Public Function  GetReference()
        Set GetReference = Me.LifeGroup.Group.Add( Me.Reference )
    End Function
End Class


Class  LifeGroupCounterClass
    Public  Group
    Public  CreatedMemberID
    Public  ReferenceCount
End Class


Class  RootHandleClass
    Public  p  '// Target object of this handle

    Private Sub  Class_Initialize()
        Set group = new LifeGroupClass
        group.AddHandle  Me, new MemberClass
    End Sub

    Private Sub  Class_Terminate()
        WScript.echo  Me.p.LifeGroup.CreatedMemberID &": RootHandleClass::Class_Terminate"

        If not IsEmpty( Me.p.LifeGroup.Group ) Then _
            Me.p.LifeGroup.Group.AddTerminated  Me.p
    End Sub
End Class


Class  LifeHandleClass
    Public  p  '// Target object of this handle

    Private Sub  Class_Terminate()
        WScript.echo  Me.p.LifeGroup.CreatedMemberID &": LifeHandleClass::Class_Terminate"

        If not IsEmpty( Me.p.LifeGroup.Group ) Then _
            Me.p.LifeGroup.Group.AddTerminated  Me.p
    End Sub
End Class


Class  LifeGroupClass
    Public   Name
    Public   Objects
    Public   TerminatedObjects
    Private  m_CreatedMemberID

    Private Sub  Class_Initialize()
        Set Me.Objects = CreateObject( "Scripting.Dictionary" )
        Set Me.TerminatedObjects = CreateObject( "Scripting.Dictionary" )
        m_CreatedMemberID = 0
    End Sub

    Private Sub  Class_Terminate()
        WScript.echo  "LifeGroupClass::Class_Terminate"
    End Sub

    Public Sub  AddHandle( Handle, Object )
        If IsEmpty( Object.LifeGroup ) Then _
            Set Object.LifeGroup = new LifeGroupCounterClass

        If not IsEmpty( Object.LifeGroup.Group ) Then
            If not Object.LifeGroup.Group Is Me Then
                Err.Raise  1, "すでに別の LifeGroup に所属しているようです"
            End If

            If Object.LifeGroup.ReferenceCount = 0 Then
                Me.TerminatedObjects.Remove  Object.LifeGroup.CreatedMemberID
            End If
            Object.LifeGroup.ReferenceCount = Object.LifeGroup.ReferenceCount + 1
        Else
            Do
                m_CreatedMemberID = m_CreatedMemberID + 1
                If not Objects.Exists( m_CreatedMemberID ) Then _
                    Exit Do
            Loop

            Set Me.Objects( m_CreatedMemberID ) = Object
            Set Object.LifeGroup.Group = Me

            Object.LifeGroup.CreatedMemberID = m_CreatedMemberID
            Object.LifeGroup.ReferenceCount = 1
        End If

        Set Handle.p = Object
    End Sub

    Public Function  Add( Object )
        Set Add = new LifeHandleClass
        Me.AddHandle  Add, Object
    End Function

    Public Sub  Remove( Object )
        If not Object.LifeGroup.Group Is Me Then  Exit Sub
        If not Me.Objects( Object.LifeGroup.CreatedMemberID ) Is Object Then  Exit Sub

        Object.DestroyReferences

        Me.Objects.Remove  Object.LifeGroup.CreatedMemberID
        Object.LifeGroup.Group = Empty
        Object.LifeGroup.CreatedMemberID = Empty
    End Sub

    Public Sub  AddTerminated( Object )
        If not Object.LifeGroup.Group Is Me Then  Exit Sub
        If not Me.Objects( Object.LifeGroup.CreatedMemberID ) Is Object Then  Exit Sub

        Object.LifeGroup.ReferenceCount = Object.LifeGroup.ReferenceCount - 1

        If Object.LifeGroup.ReferenceCount = 0 Then
            Set Me.TerminatedObjects( Object.LifeGroup.CreatedMemberID ) = Object

            If Me.TerminatedObjects.Count = Me.Objects.Count Then
                For Each  a_object  In Me.Objects.Items
                    a_object.DestroyReferences
                    a_object.LifeGroup = Empty
                Next
                Me.Objects.RemoveAll
                Me.TerminatedObjects.RemoveAll
            End If
        End If
    End Sub
End Class
ルート オブジェクトの中にある、相互参照しているオブジェクトのグループとなるオブジェクト。
グループに所属しているオブジェクト(ルート オブジェクトも含む)のハンドルがすべて参照され
なくなったら、相互参照を切るメソッド DestroyReferences を呼び出し、Class_Terminate が
呼び出されます。
ハンドルのメンバー p を介してオブジェクトを操作する必要があります。
A
B body
A body
B
LifeGroup
LifeHandleClass
p
LifeGroup
p
CaseA:
1: RootHandleClass::Class_Terminate
2: LifeHandleClass::Class_Terminate
A1: MemberClass::Class_Terminate
LifeGroupClass::Class_Terminate
A2: MemberClass::Class_Terminate
Returned

CaseB:
1: RootHandleClass::Class_Terminate
Returned
2: LifeHandleClass::Class_Terminate
B1: MemberClass::Class_Terminate
LifeGroupClass::Class_Terminate
B2: MemberClass::Class_Terminate

CaseC:
1: RootHandleClass::Class_Terminate
2: LifeHandleClass::Class_Terminate
C1: MemberClass::Class_Terminate
LifeGroupClass::Class_Terminate
C2: MemberClass::Class_Terminate
Returned
: LifeHandleClass::Class_Terminate
C3: MemberClass::Class_Terminate

CaseD:
1: RootHandleClass::Class_Terminate
2: LifeHandleClass::Class_Terminate
Returned
1: LifeHandleClass::Class_Terminate
D2: MemberClass::Class_Terminate
LifeGroupClass::Class_Terminate
D1: MemberClass::Class_Terminate

CaseE:
2: LifeHandleClass::Class_Terminate
1: RootHandleClass::Class_Terminate
2: LifeHandleClass::Class_Terminate
E1: MemberClass::Class_Terminate
LifeGroupClass::Class_Terminate
E2: MemberClass::Class_Terminate
Returned

CaseF:
1: RootHandleClass::Class_Terminate
2: LifeHandleClass::Class_Terminate
F1: MemberClass::Class_Terminate
LifeGroupClass::Class_Terminate
F2: MemberClass::Class_Terminate
1: RootHandleClass::Class_Terminate
2: LifeHandleClass::Class_Terminate
F1: MemberClass::Class_Terminate
LifeGroupClass::Class_Terminate
F2: MemberClass::Class_Terminate
Returned
End of Main
もし、ここ以降でデストラクターが動くときは、プログラム終了時にすべてのオブジェク
トを削除することによるデストラクター呼び出しです。
表示例:
検証コード
RootHandleClass
関連
参考
CaseA
WScript.echo  "Returned"

Set object = CaseB()
WScript.echo  "Returned"
object = Empty

Set object = CaseC()
WScript.echo  "Returned"
object = Empty

CaseD
WScript.echo  "Returned"

WScript.echo  "End of Main"
WScript.echo  "もし、ここ以降でデストラクターが動くときは、"+_
    "プログラム終了時にすべてのオブジェクトを削除する"+_
    "ことによるデストラクター呼び出しです。"


Sub  CaseA()  '// 相互参照しているオブジェクトの削除
    WScript.echo  vbCRLF +"CaseA:"
    Set group = new LifeGroupClass : group.Name = "CaseA"
    Set a_handle = group.Add( new MutualClass ) : a_handle.p.Name = "A1"
    Set b_handle = group.Add( new MutualClass ) : b_handle.p.Name = "A2"
    Set a_handle.p.Reference = b_handle.p
    Set b_handle.p.Reference = a_handle.p
End Sub

Function  CaseB()  '// ハンドルを返すとき
    WScript.echo  vbCRLF +"CaseB:"
    Set group = new LifeGroupClass : group.Name = "CaseB"
    Set a_handle = group.Add( new MutualClass ) : a_handle.p.Name = "B1"
    Set b_handle = group.Add( new MutualClass ) : b_handle.p.Name = "B2"
    Set a_handle.p.Reference = b_handle.p
    Set b_handle.p.Reference = a_handle.p
    Set CaseB = a_handle
End Function

Function  CaseC()  '// LefeGroup から外れるとき
    WScript.echo  vbCRLF +"CaseC:"
    Set group = new LifeGroupClass : group.Name = "CaseC"
    Set a_handle = group.Add( new MutualClass ) : a_handle.p.Name = "C1"
    Set b_handle = group.Add( new MutualClass ) : b_handle.p.Name = "C2"
    Set c_handle = group.Add( new MutualClass ) : c_handle.p.Name = "C3"
    group.Remove  c_handle.p
    Set a_handle.p.Reference = b_handle.p
    Set b_handle.p.Reference = a_handle.p
    Set CaseC = c_handle
End Function

Sub  CaseD()  '// LefeGroup を再利用するとき
    WScript.echo  vbCRLF +"CaseD:"
    Set group = new LifeGroupClass : group.Name = "CaseD"
    For count = 1  To 2
        Set a_handle = group.Add( new MutualClass ) : a_handle.p.Name = "D1"
        Set b_handle = group.Add( new MutualClass ) : b_handle.p.Name = "D2"
        Set a_handle.p.Reference = b_handle.p
        Set b_handle.p.Reference = a_handle.p

        a_handle = Empty
        b_handle = Empty
    Next
End Sub


Class  MutualClass
    Public  Name
    Public  Reference

    Public  LifeGroup          '// Don't write except for LifeGroupClass
    Public  LifeGroupMemberID  '// Don't write except for LifeGroupClass

    Private Sub  Class_Terminate()
        WScript.echo  Me.Name +": MutualClass::Class_Terminate"
    End Sub

    Public Sub  DestroyReferences()
        Me.Reference = Empty
    End Sub
End Class


Class  LifeHandleClass
    Public  p  '// Target object of this handle

    Private Sub  Class_Terminate()
        WScript.echo  Me.p.LifeGroupMemberID &": LifeHandleClass::Class_Terminate"

        If not IsEmpty( Me.p.LifeGroup ) Then _
            Me.p.LifeGroup.AddTerminated  Me.p
    End Sub
End Class


Class  LifeGroupClass
    Public   Name
    Public   Objects
    Public   TerminatedObjects
    Private  m_CreatedMemberID

    Private Sub  Class_Initialize()
        Set Me.Objects = CreateObject( "Scripting.Dictionary" )
        Set Me.TerminatedObjects = CreateObject( "Scripting.Dictionary" )
        m_CreatedMemberID = 0
    End Sub

    Private Sub  Class_Terminate()
        WScript.echo  "LifeGroupClass::Class_Terminate"
    End Sub

    Public Function  Add( Object )
        If not IsEmpty( Object.LifeGroup ) Then
            If Object.LifeGroup Is Me Then  Set Add = Nothing : Exit Function
            Err.Raise  1, "すでに別の LifeGroup に所属しているようです"
        End If

        Do
            m_CreatedMemberID = m_CreatedMemberID + 1
            If not Objects.Exists( m_CreatedMemberID ) Then _
                Exit Do
        Loop

        Set Me.Objects( m_CreatedMemberID ) = Object
        Set Object.LifeGroup = Me
        Object.LifeGroupMemberID = m_CreatedMemberID

        Set Add = new LifeHandleClass
        Set Add.p = Object
    End Function

    Public Sub  Remove( Object )
        If not Object.LifeGroup Is Me Then  Exit Sub
        If not Me.Objects( Object.LifeGroupMemberID ) Is Object Then  Exit Sub

        Object.DestroyReferences

        Me.Objects.Remove  m_CreatedMemberID
        Object.LifeGroup = Empty
        Object.LifeGroupMemberID = Empty
    End Sub

    Public Sub  AddTerminated( Object )
        If not Object.LifeGroup Is Me Then  Exit Sub
        If not Me.Objects( Object.LifeGroupMemberID ) Is Object Then  Exit Sub

        Set Me.TerminatedObjects( Object.LifeGroupMemberID ) = Object

        If Me.TerminatedObjects.Count = Me.Objects.Count Then
            For Each  a_object  In Me.Objects.Items
                a_object.DestroyReferences
                a_object.LifeGroup = Empty
                a_object.LifeGroupMemberID = Empty
            Next
            Me.Objects.RemoveAll
            Me.TerminatedObjects.RemoveAll
        End If
    End Sub
End Class
相互参照しているオブジェクトのグループとなるオブジェクト。
グループに所属しているオブジェクトのハンドルがすべて参照されなくなったら、
相互参照を切るメソッド DestroyReferences を呼び出し、Class_Terminate が呼び出されます。
ハンドルのメンバー p を介してオブジェクトを操作する必要があります。
A
B body
A body
B
LifeGroup
LifeHandleClass
p
p
CaseA:
1: LifeHandleClass::Class_Terminate
2: LifeHandleClass::Class_Terminate
A1: MutualClass::Class_Terminate
LifeGroupClass::Class_Terminate
A2: MutualClass::Class_Terminate
Returned

CaseB:
2: LifeHandleClass::Class_Terminate
Returned
1: LifeHandleClass::Class_Terminate
B2: MutualClass::Class_Terminate
LifeGroupClass::Class_Terminate
B1: MutualClass::Class_Terminate

CaseC:
1: LifeHandleClass::Class_Terminate
2: LifeHandleClass::Class_Terminate
C1: MutualClass::Class_Terminate
LifeGroupClass::Class_Terminate
C2: MutualClass::Class_Terminate
Returned
: LifeHandleClass::Class_Terminate
C3: MutualClass::Class_Terminate

CaseD:
1: LifeHandleClass::Class_Terminate
2: LifeHandleClass::Class_Terminate
D1: MutualClass::Class_Terminate
D2: MutualClass::Class_Terminate
3: LifeHandleClass::Class_Terminate
4: LifeHandleClass::Class_Terminate
D1: MutualClass::Class_Terminate
D2: MutualClass::Class_Terminate
LifeGroupClass::Class_Terminate
Returned
End of Main
もし、ここ以降でデストラクターが動くときは、プログラム終了時にすべてのオブジェク
トを削除することによるデストラクター呼び出しです。
表示例:
検証コード
Case4
WScript.echo  "End of Main"
WScript.echo  "もし、ここ以降でデストラクターが動くときは、"+_
    "プログラム終了時にすべてのオブジェクトを削除する"+_
    "ことによるデストラクター呼び出しです。"

Sub  Case4()
    WScript.echo  "Case4:"
    Set a_object = new A_Class : a_object.CaseName = "Case4"
    Set b_object = new B_Class : b_object.CaseName = "Case4"
    Set a_object.Reference = b_object
    Set b_object.Reference = a_object.Body  '// ここが a_object では呼び出されなくなる
End Sub


Class  A_Class
    Public  Body

    Private Sub  Class_Initialize()
        Set Me.Body = new A_BodyClass
    End Sub

    Private Sub  Class_Terminate()
        Me.Body.Reference = Empty
    End Sub

    Public Property Get  CaseName() : CaseName = Body.CaseName : End Property
    Public Property Let  CaseName( x ) : Body.CaseName = x : End Property

    Public Property Get  Reference() : Set Reference = Body.Reference : End Property
    Public Property Set  Reference( x ) : Set Body.Reference = x : End Property
End Class

Class  A_BodyClass
    Public  CaseName
    Public  Reference

    Private Sub  Class_Terminate()
        WScript.echo  Me.CaseName +": A_Class::Class_Terminate"
    End Sub
End Class

Class  B_Class
    Public  CaseName
    Public  Reference

    Private Sub  Class_Terminate()
        WScript.echo  Me.CaseName +": B_Class::Class_Terminate"
    End Sub
End Class
検証コード
相互参照しているオブジェクトが、Class_Terminate を呼び出せるようにしたクラス。
このクラスのオブジェクトが削除されたとき、相互参照しているオブジェクトも削除される。
Case4:
Case4: B_Class::Class_Terminate
Case4: A_Class::Class_Terminate
End of Main
表示例:
B
A
A body
相互参照を切る
Case1
WScript.echo  "End of Main"
Case2
WScript.echo  "End of Main"
WScript.echo  "もし、ここ以降でデストラクターが動くときは、"+_
    "プログラム終了時にすべてのオブジェクトを削除する"+_
    "ことによるデストラクター呼び出しです。"

Sub  Case1()
    WScript.echo  vbCRLF +"Case1:"
    Set destroyer = new DestroyerClass
    Set a_object = new A_Class : a_object.CaseName = "Case1" : destroyer.Add  a_object
    Set b_object = new B_Class : b_object.CaseName = "Case1" : destroyer.Add  b_object
    Set a_object.Reference = b_object
    Set b_object.Reference = a_object
End Sub

Sub  Case2()
    WScript.echo  vbCRLF +"Case2:"
    Set destroyer = new DestroyerClass
    Set a_object = new A_Class : a_object.CaseName = "Case2"
    Set b_object = new B_Class : b_object.CaseName = "Case2"
    Set a_object.Reference = b_object
    Set b_object.Reference = a_object
End Sub


Class  DestroyerClass
    Public  Objects

    Private Sub  Class_Initialize()
        Set Me.Objects = new ObjectSetClass
    End Sub

    Private Sub  Class_Terminate()
        WScript.echo  "DestroyerClass::Class_Terminate"
        For Each  item  In Me.Objects.Objects.Items
            item.DestroyReferences
        Next
    End Sub

    Public Sub  Add( Object )
        Me.Objects.Add  Object
        Object.IsDestroyer = True
    End Sub

    Public Sub  Remove( Object )
        Me.Objects.Remove  Object
        Object.IsDestroyer = False
    End Sub
End Class

Sub  CheckUnderDestroyer( Object )
    If not Object.IsDestroyer Then _
        Err.Raise 1,, "このオブジェクトは相互参照しているため、DestroyerClass の管理下に置く必要があります。"
End Sub


Class  A_Class
    Public  CaseName
    Public  Reference
    Public  IsDestroyer    '// ■追加

    Private Sub  Class_Terminate()
        WScript.echo  Me.CaseName +": A_Class::Class_Terminate"
        CheckUnderDestroyer  Me  '// ■追加
    End Sub

    Public Sub  DestroyReferences()  '// ■追加
        Me.Reference = Empty
    End Sub
End Class

Class  B_Class
    Public  CaseName
    Public  Reference
    Public  IsDestroyer    '// ■追加

    Private Sub  Class_Terminate()
        WScript.echo  Me.CaseName +": B_Class::Class_Terminate"
        CheckUnderDestroyer  Me  '// ■追加
    End Sub

    Public Sub  DestroyReferences()  '// ■追加
        Me.Reference = Empty
    End Sub
End Class


Class  ObjectSetClass
    Public   Objects
    Private  m_NextID

    Private Sub  Class_Initialize()
        Set Me.Objects = CreateObject( "Scripting.Dictionary" )
        m_NextID = 0
    End Sub

    Public Sub  Add( Object )
        For Each  item  In Me.Objects.Items
            If item is Object Then  Exit Sub
        Next

        Do
            m_NextID = m_NextID + 1
            If not Objects.Exists( CStr( m_NextID ) ) Then _
                Exit Do
        Loop
        Set Me.Objects( CStr( m_NextID ) ) = Object
    End Sub

    Public Sub  Remove( Object )
        For Each  key  In Me.Objects.Keys
            If Objects( key ) is Object Then
                Me.Objects.Remove  key
                Exit Sub
            End If
        Next
    End Sub
End Class
相互参照しているオブジェクトを削除するタイミングを制御するオブジェクト。
DestroyerClass のオブジェクトが削除されるときに、相互参照を切るメソッド DestroyReferences
を呼び出し、Class_Terminate が呼び出されます。
検証コード
Destroyer
A
B
Case1:
DestroyerClass::Class_Terminate
Case1: A_Class::Class_Terminate
Case1: B_Class::Class_Terminate
End of Main

Case2:
DestroyerClass::Class_Terminate
End of Main
もし、ここ以降でデストラクターが動くときは、プログラム終了時にすべてのオブジェク
トを削除することによるデストラクター呼び出しです。
Case2: A_Class::Class_Terminate
C:\Users\user1\Desktop\MutualRef.vbs(65, 3) Microsoft VBScript 実行時エラー: こ
のオブジェクトは相互参照しているため、DestroyerClass の管理下に置く必要がありま
す。

Case2: B_Class::Class_Terminate
C:\Users\user1\Desktop\MutualRef.vbs(65, 3) Microsoft VBScript 実行時エラー: こ
のオブジェクトは相互参照しているため、DestroyerClass の管理下に置く必要がありま
す。
表示例:
相互参照を切る
関連