Tutorial
Type als Objekt - Das UDT-Tutorial Teil 2
von MOD | Seite 2 von 3 |
Copy Constructor
Ein Copy Constructor ist ein spezieller Constructor-Typ, der zum Erstellen einer Kopie eines bestehenden Objektes dient. Mit einem Code wie diesem:
Type foo
'...
End Type
Dim As foo a
Dim As foo b = a
Was passiert ist, dass FreeBASIC automatisch versteckten Code zum Erstellen von b(als Kopie von a) erzeugt. Dies ist der Standard-Copy Constructor, und kopiert einfach nur die Datenfelder (Elemente) rüber. Wir können auch unseren eigenen Copy Constructor definieren. Hier folgt ein kurzer Schnipsel, der zeigt, wie er deklariert wird.
Type foo
Declare Constructor(ByRef obj As foo)
'...
End Type
Das Thema vertiefen wir doch gleich mal.
Tiefe/Oberflächliche Kopie
Im vorigen Beispiel haben wir mit dem Code 'Dim As foo b = a', das benutzt, was man Shallow Copy oder eben Oberflächliche Kopie nennt, die Datenfelder werden einfach nur kopiert, manchmal ist die jedoch nicht erwünscht. Was würde passieren, wenn eins der Elemente ein Pointer ist? Nun, die Adresse auf die der Pointer zeigt würde kopiert werden, mit dem Effekt, dass beide Objekte auf exakt denselben Speicherbereich zeigen. Ein Beispiel dafür:
Type foo
x As Integer Ptr
End Type
Dim As foo a
a.x = Allocate(SizeOf(Integer))
*a.x = 42
Dim As foo b = a
Print *a.x, *b.x
*a.x = 420
Print *a.x, *b.x
DeAllocate(a.x)
Weil beide auf den selben Speicherbereich zeigen, betreffen Änderungen bei einem auch den anderen. Wie schon im vorigen Kapitel beim Copy Constructor erklärt, erzeugt FreeBASIC standardmäßig den Code für oberflächliche Kopien. Das stimmt auch auf Zuweisung zu, wie z.B.:
Dim As foo a, b
b = a
Auch in diesem Fall erzeugt FreeBASIC einen Standard-Zuweisungsoperator (Let) zum Durchführen der Shallow Copy. Um 'Deep Copies', also Tiefe Kopien zu machen, müssen wir einen Copy Constructor definieren und einen Zuweisungsoperator überladen, um unseren Type zu akzeptieren.
Hier ein Verwendungsbeispiel:
Type foo
Declare Constructor()
Declare Constructor(ByRef obj As foo)
Declare Destructor()
Declare Operator Let(ByRef obj As foo)
x As Integer Ptr
End Type
Constructor foo()
Print "Default ctor"
x = Callocate(SizeOf(Integer))
End Constructor
Constructor foo(ByRef obj As foo)
Print "Copy ctor"
x = Callocate(SizeOf(Integer))
*x = *obj.x
End Constructor
Destructor foo()
Print "dtor"
DeAllocate(x)
End Destructor
Operator foo.Let(ByRef obj As foo)
Print "Let"
*x = *obj.x
End Operator
Dim As foo a
*a.x = 42
Dim As foo b = a 'Uses the copy constructor
Print *a.x, *b.x
*a.x = 420
Print *a.x, *b.x
Man erkennt, dass der Copy Constructor in der Zeile 'Dim As foo b = a' aufgerufen wird und diesmal reservieren wir etwas Speicher und kopieren die Daten in den neuen Copy Constructor, sodass wir x in einem Objekt ändern können, ohne dies auch im anderen zu tun.
Wenn wir den Haupt-Code wie folgt ändern:
Dim As foo a, b
*a.x = 42
b = a 'Der Zuweisungsoperator (Let) wird diesmal hier benutzt
Print *a.x, *b.x
*a.x = 420
Print *a.x, *b.x
dann wird diesmal der Zuweisungsoperator benutzt. Beachte, dass wir im Zuweisungsoperator-Code keinen Speicher zu reservieren brauchen, da dies schon der Standard-Constructor gemacht hat, wir müssen also nur die Daten kopieren. Die Zeile '*x = *obj.x' führt diese Kopie durch. Wenn wir etwas Komplizierteres hätten, wie ein dynamisches Speicher-Array, dann müssten wir auch den Speicher neu reservieren, damit er die korrekte Größe für die zu kopierenden Daten hat. Hier ist ein fortgeschritteneres Beispiel, um das zu zeigen:
Type foo
Declare Constructor(ByVal num_elements As Integer)
Declare Constructor(ByRef obj As foo)
Declare Destructor()
Declare Operator Let(ByRef obj As foo)
x As Integer Ptr
size As Integer
End Type
Constructor foo(ByVal num_elements As Integer)
Print "Default ctor"
x = Callocate(SizeOf(Integer) * num_elements)
size = num_elements
End Constructor
Constructor foo(ByRef obj As foo)
Print "Copy ctor"
x = Callocate(SizeOf(Integer) * obj.size)
size = obj.size
For i As Integer = 0 To size - 1
x[i] = obj.x[i]
Next i
End Constructor
Destructor foo()
Print "dtor"
DeAllocate(x)
End Destructor
Operator foo.Let(ByRef obj As foo)
Print "Let"
x = ReAllocate(x, SizeOf(Integer) * obj.size)
size = obj.size
For i As Integer = 0 To size - 1
x[i] = obj.x[i]
Next i
End Operator
Dim As foo a = foo(5)
a.x[0] = 42
a.x[1] = 420
Dim As foo b = a 'benutzt den Copy Constructor
Print a.x[0], a.x[1], b.x[0], b.x[1]
b.x[0] = 10
b.x[1] = 20
Print a.x[0], a.x[1], b.x[0], b.x[1]
b = a 'benutzt jetzt den Zuweisungsoperator
Print a.x[0], a.x[1], b.x[0], b.x[1]
Das mag auf den ersten Blick sehr komplex erscheinen, aber es ist es wert, nochmal gelesen zu werden und mit den Beispielen zu experimentieren. Es ist nicht mehr sehr geheimnisvoll, wenn man sich dran gewöhnt hat.
Zusätzliche Informationen und Funktionen | |||||||
---|---|---|---|---|---|---|---|
|