That’s true. However, I think it’s confusing (to me at least) to think of it that way. The C++ experience I have helps with this.
When in BASIC we do ‘Dim i As Integer’, we are doing two things: we establish a token for an integer called ‘i’, and internally there’s memory allocated to it. We are pretty careless with this memory, but courtesy of Rainieri the token goes out of scope and the memory deallocated perfectly.
When we establish an array ‘Dim MyObj() as CDevil’ and ‘ReDim MyObj(665)’ you are just creating a container to hold 666 devils. If you immediately call MyObj(7).GoToHell, you get an exception because no object has been created for that index. All you’ve done is set up the container, not the objects. An array is just a container. All individual objects in that array have their own lives and own memory.
What you are doing, like Joe said, is making two references to the same array. Given my evil example above, and pretending you’ve created 666 Devils, you have 1332 objects/tokens, but only 666 discreet ones, and only 1 discreet array, not 2. So setting A(77).HasPitchFork = True also makes B’s 77th indexed object have a pitchfork too. And… A.Remove(144) also removes a devil from B().
BASIC is nice because it cleans up memory for you when the variable (the token) goes out of scope. That’s the “magic” of reference counting. And ultimately when your app quits, all memory is cleaned up because everything goes out of scope. (Yes, I’m oversimplifying.) So when you say “memory leak”, you are mostly talking about during app existence, not losing memory ‘forever’.
In your example, if A() goes out of scope and B() doesn’t, the 666 pieces of memory still exist. When B() goes out of scope, all reference counts will go to 0 and memory will be cleaned up. And obviously the miniscule memory the array container itself takes up will be deallocated.
So there’s really no ‘magic’, to diagnose your situation, simply try to figure out if there are any references to the original object still in scope. This is a good thing bcause it makes your programming much more intentional, instead or willy-nilly flying objects around, just because you can, because BASIC is counting references for you. As you know, it’s easy to start making copies and you can create a “leak” - it’s not really a leak - by having some globally-scoped array still containing valid objects and you aren’t using them.
BTW, C++ trains you well in this because it is not reference counted. If you make an array, and ‘new()’ all the objects, and start make copies of of those objects, and you ‘delete()’ those copies, you’ve deallocated the memory to the originals and other copies and you crash and burn when you access those objects that are still in scope but have their memory deallocated. Before you think C++ is lousy, consider that it forces you to be disciplined and be aware of where your objects exist and where they don’t, and WHO is the legal entity allowed to deallocate those objects. BASIC does the work for you, but don’t let that make you become irresponsible where your own app becomes internally chaotic. You start using objects that are actually some other object, and you go - “what was that???”
I’m also skipping the concept of circular references, but again it’s the same solution. Know where your objects are and intentionally know when they are/should go out fo scope and be deallocated.
Sorry to “go basic” here, but just having a good grasp of memory allocation, even though BASIC is handling it for you, clears the matter up and makes the ‘magic’ go away.