0

i have an issue with dynamic arrays being passed to class byVal instead byRef, so simplified class, cArray

Option Explicit

Private mArray() As String

Public Sub init(ByRef iArray() As String)
    mArray = iArray
End Sub

Public Property Get count() As Long
    count = UBound(mArray) - LBound(mArray)
End Property

Public Property Get item(iIndex As Long) As String
    item = mArray(iIndex)
End Property

and simple function in module

Private Sub arrTest()
    Dim arr() As String, cont As cArray
    ReDim arr(0 To 1)

    arr(0) = "value0"
    arr(1) = "value1"

    Set cont = New cArray
    cont.init arr

    arr(1) = "newValue1"
    Debug.Print cont.item(1), arr(1) 'will print value1, newValue1 even though is expected to be same

    ReDim Preserve arr(0 To 2)
    arr(2) = "value2"

    Debug.Print cont.count 'will print 1
End Sub

so, question is, is this bug? normal behavior? something else?

tsolina
  • 141
  • 15
  • This is normal - an object will typically have a longer lifespan than an array, so this prevents the array's memory from being freed by anything other than the object itself. – Comintern Jan 09 '17 at 14:10
  • @TimWilliams - consider `Dim x() As Long: someobj.ArrayProperty = x: Erase x`. How is `someobj` supposed to know whether `x` has been freed or not? Arrays aren't reference types in VBA, so there isn't really a safe way to determine if the calling code or the object is responsible for cleaning up the array's memory allocation (they aren't reference counted). Dynamic arrays in particular would problematic in this regard because they can be `ReDim`'d. – Comintern Jan 09 '17 at 17:05
  • 1
    The issue is you're making an assignment of `Array` to `mArray` - that is a *by value* assignment (ie. it creates a copy of the array in `mArray` instead of creating an additional pointer to the original array you passed in). So, you can make changes to `iArray` in the `init` Sub and those will be reflected in `arrTest` but that does not create any link between arr and mArray – Tim Williams Jan 09 '17 at 17:05
  • @Comintern - yes I figured it out: thanks for the explanation. Also - http://stackoverflow.com/questions/16323776/copy-an-array-reference-in-vba but that's more about Variants than Arrays... – Tim Williams Jan 09 '17 at 17:08
  • I asked a similar question, which might have helpful answers for you: http://stackoverflow.com/questions/25328975/array-as-a-class-member – Blackhawk Jan 09 '17 at 23:12

1 Answers1

0

Actually, the array is passed by reference. The problem comes at the assignment.

In VBA assigning one array variable to another array variable or one string variable to another string variable creates a copy though there are ways around this using Variants or CopyMemory, for example. If you're interested, drop a comment.

I can demonstrate this by using VarPtr to get the actual addresses for comparison. Let's add a few lines to your code...

cArray

Option Explicit

Private mArray() As String

Public Sub init(ByRef iArray() As String)
    Debug.Print "The address of the function parameter is: " & VarPtr(iArray(0)) '<----- add this line
    mArray = iArray
    Debug.Print "The address of the mArray class member is: " & VarPtr(mArray(0)) '<----- add this line
End Sub

Public Property Get count() As Long
    count = UBound(mArray) - LBound(mArray)
End Property

Public Property Get item(iIndex As Long) As String
    item = mArray(iIndex)
End Property

mdlMain

Private Sub arrTest()
    Dim arr() As String, cont As cArray
    ReDim arr(0 To 1)

    arr(0) = "value0"
    arr(1) = "value1"

    Debug.Print "The address of the newly dimensioned and initialized arr is: " & VarPtr(arr(0)) '<----- add this line

    Set cont = New cArray
    cont.init arr

    arr(1) = "newValue1"

    Debug.Print cont.item(1), arr(1) 'will print value1, newValue1 even though is expected to be same

    ReDim Preserve arr(0 To 2)
    arr(2) = "value2"

    Debug.Print cont.count 'will print 1
End Sub

When I run this, I get (the memory addresses will likely be different for you):

The address of the newly dimensioned and initialized arr is: 192524056
The address of the function parameter is: 192524056 The address of the
mArray class member is: 192524040 
value1        newValue1  1

So you can see that the actual function parameter WAS passed by reference, but the assignment created a copy.

Blackhawk
  • 5,984
  • 4
  • 27
  • 56