Comment réaliser des allocations dynamiques ?
Le fonctionnement de très nombreuses applications repose sur l'utilisation des tableaux. Lorsque le nombre d'éléments contenus est connu lors de la réalisation du logiciel, des tableaux de dimensions fixes peuvent être employés et on parle alors d'allocation statique de mémoire. Cependant, il arrive très fréquemment que le nombre d'éléments auxquels l'application doit référer ne soit pas connu à l'avance. Il faut alors, durant l'exécution, ajouter à un tableau des éléments, opération connue sous le nom d'allocation dynamique. Nous allons ici discuter des techniques permettant en VB de réaliser une telle allocation dynamique. Les objetsL'allocation dynamique, ou création, d'objets de VB est principalement réalisée par le mot clé New, suivi du type de l'objet à allouer. D'autres classes sont construites par une fonction spécifique du modèle objet utilisé. L'affectation d'un objet à une variable est effectuée par le mot clé Set. Dim a As Object Set a = New Collection
Set a = LoadPicture("c:\images\toto.jpg")
L'allocation d'objet en elle-même est donc assez simple. Il est néanmoins souvent nécessaire de garder trace d'objets précédemment créés dans des structures dynamiques, dont des Collections. Le tableau dynamiqueVisual Basic permet l'allocation dynamique de tableaux à l'aide de l'instruction Redim. Il est important de constater que l'utilisation de Redim force la réallocation et, par conséquent, les données précédemment contenues seront simplement perdues (nous verrons plus loin comment palier à cet inconvénient). Il est à noter que chaque réallocation a un coût en temps d'exécution. Plus la plage à allouer est grande, plus il sera difficile pour le système d'exploitation de trouver et préparer une zone mémoire suffisamment large. Dim a() As Integer Dim i As Long ReDim a(17) a(5) = 4 For i = LBound(a) To UBound(a) Debug.Print i, a(i) Next i ReDim a(18, 5) a(3, 4) = 5 ReDim a(19, 7) Debug.Print "Elément 3,4", a(3, 4)
Comment empêcher la destruction des données lors d'une réallocationLorsqu'uniquement la dernière dimension du tableau change, il est possible de forcer Visual Basic à recopier les valeurs précédemment contenues à l'aide du mot clé Preserve. Dans les autres cas de redimensionnement, s'il faut assurer la persistance des données initialement inscrites, la copie devra être effectuée manuellement. Dans tous les cas, chaque copie de données a elle aussi un coût en temps d'exécution. Voici un exemple d'utilisation : Dim a() As Long
ReDim a(5) a(3) = 555
ReDim Preserve a(10) a(8) = 999
Debug.Print a(3)
Mise en œvreLe code suivant résume, en un exemple concret, l'utilisation et les recommandations d'utilisation d'allocations dynamique de tableau à l'aide de Redim et Redim Preserve. Il s'agit de filtrer une série de données pour ne conserver que celles entre deux bornes : Private Function Filtre(a() As Integer, Min As Integer, Max As Integer) As Integer() Dim Result() As Integer Dim Count As Long Dim i As Long ReDim Result(LBound(a) To UBound(a))
For i = LBound(a) To UBound(a) If (a(i) >= Min And a(i) <= Max) Then Result(LBound(a) + Count) = a(i) Count = Count + 1 End If Next i If (Count = 0) Then Erase Result Else ReDim Preserve Result(LBound(Result) To (LBound(Result) + Count - 1)) End If Filtre = Result End Function
Tableau de tableaux, ou le tableau dynamique d'UDT dynamiquesPlusieurs applications nécessitent l'utilisation de tableau de tableaux, afin d'obtenir pour chaque entrée plusieurs sous-entrées donc le nombre n'est, au départ, pas défini. Dans certains cas, notamment le cas d'école de la matrice symétrique, il est possible d'aplanir les données dans un seul vecteur après quelques manipulations mathématiques. L'idée est d'établir une relation de correspondance ente une entrée x,y de la matrice et l'index du vecteur. Ce genre de résolution n'a malheureusement que de rares applications. Dans un cadre plus général, Visual Basic de réaliser des tableaux de tableaux en utilisant des UDT (User Defined Type) contenant eux-même un tableau dynamique. Cette technique est illustrée par le stockage de gains, reçu par reçu, par exemple pour un magasin. On ne sait, a priori, pas combien de récépissés seront stockés, ni combien d'objets achetés seront indiqués sur le ticket. Nous emploierons donc un tableau dynamique de reçus contenant chacun un tableau dynamique de prix. Option Explicit
Private Type BoughtItems ItemPrice() As Long End Type
Private Receipts() As BoughtItems
Private Function MyUBoundBoughtItems(Data() As BoughtItems) As Long MyUBoundBoughtItems = -1 On Error Resume Next MyUBoundBoughtItems = UBound(Data) End Function
Private Function MyUBoundLong(Data() As Long) As Long MyUBoundLong = -1 On Error Resume Next MyUBoundLong = UBound(Data) End Function
Private Function ReceiptTotal(Receipt As Long) As Long Dim i As Long ReceiptTotal = 0
If (Receipt <= MyUBoundBoughtItems(Receipts) And Receipt > -1) Then For i = 0 To MyUBoundLong(Receipts(Receipt).ItemPrice) ReceiptTotal = ReceiptTotal + Receipts(Receipt).ItemPrice(i) Next i Else Err.Raise 9 End If End Function
Private Function NewReceipt() As Long If (MyUBoundBoughtItems(Receipts) = -1) Then ReDim Receipts(0) Else ReDim Preserve Receipts(MyUBoundBoughtItems(Receipts) + 1) End If NewReceipt = MyUBoundBoughtItems(Receipts) End Function
Private Sub AddItem(Amount As Long) Dim CurrentDayIndex As Long, CurrentTransactionIndex As Long If (MyUBoundBoughtItems(Receipts) <> -1) Then With Receipts(MyUBoundBoughtItems(Receipts)) If MyUBoundLong(.ItemPrice) = -1 Then ReDim .ItemPrice(0) Else ReDim Preserve .ItemPrice(MyUBoundLong(.ItemPrice) + 1) End If .ItemPrice(MyUBoundLong(.ItemPrice)) = Amount End With Else Err.Raise 100, , "No receipt exists!" End If End Sub
Public Sub Commerce() Dim i As Long
Erase Receipts
NewReceipt AddItem 10 AddItem 15 AddItem 20 AddItem 25 NewReceipt AddItem 2 NewReceipt AddItem 40 AddItem 3 AddItem 5 For i = 0 To MyUBoundBoughtItems(Receipts) Debug.Print "Montant du ticket " & i & " : " & ReceiptTotal(i) Next i End Sub
Bien entendu, dépendant du type d'opérations à effectuer ainsi que des contraintes de vitesses, il est tout aussi possible de réaliser la même opération à l'aide de tableaux de collections, collections de tableaux et collections de collections. L'UDT devient alors facultatif. Fonction de ce que doit réaliser le logiciel, il est aussi possible d'utiliser des classes en place des UDT. Pour aller plus loin Voir aussi : |