Kleiner OOP Kurs mit Purebasic. Ohne Macros und PreCompilers

Hier kannst du häufig gestellte Fragen/Antworten und Tutorials lesen und schreiben.
Benutzeravatar
mk-soft
Beiträge: 3691
Registriert: 24.11.2004 13:12
Wohnort: Germany

Kleiner OOP Kurs mit Purebasic. Ohne Macros und PreCompilers

Beitrag von mk-soft »

Kleiner OOP Kurs mit Purebasic.

Machmal ist es vom Vorteil auch unter Purebasic mit Objekten zu arbeiten.
Purebasic unterstütz von sich aus die Verwendung von externen Objekten. Somit auch die Programmierung von eigenen Objekten.

Der Aufwand ist ein wenig größer, aber durchaus im sinnvollen Rahmen verwendbar.

Hier schon mal ein kleiner Anfang. Bitte meine Beschreibung überarbeiten.

Basis Klasse

Code: Alles auswählen

;- TOP

; OOP Programmierung unter Purebasic
;
; Programmierung von Klassen ohne Macros oder Pre-Compiler. Dazu bieten sich Module zur Kapelsung gut an.
;
; Grundlagen:
; - Interfaces
;     Interfaces sind eine Beschreibung der Funktionen (Methoden) und Paramter der Klasse.
;     Die Funktionen (Methoden) der Klasse werden immer indirekt über eine Tabelle mit der Funktionsadresse aufgerufen.
;     Die Tabelle mit den Funktionsadressen muss immer die gleiche Reihenfolge haben wie diese im Interface
;     beschrieben wurde.
;
; - Struktur:
;     Zur Verwendung einer Klasse benötigen wir eine Struktur. Diese muss immer folgenden Aufbau haben.
;       + *vTable : Ein Zeiger auf die Tabelle mit Zeigern auf die Funktionen der Klasse.
;                   Diese muss immer an der ersten Stelle vorhanden sein.
;       + Daten   : Dahinter können die Daten der Klasse angelegt werden. 
; 
; - Funktion (Methode):
;     Wird eine Funktion (Methode) der Klasse aufgerufen, ist der erste Parameter IMMER ein Zeiger auf sich selbst (*This).
;     Danach kommen erst die Paramter wie diese im Interface beschrieben sind.
;
; - Objekt:
;     Um ein Objekt aus der Klasse zu erstellen benötigen wir eine Funktion die das Objekt anlegt.
;     Diese können wir zum Beispiel 'New' oder 'Create' nennen.
;     Diese Funktion muss den erforderlichen Speicher für das Objekt anlegen und den Zeiger auf die Funktiontabelle eintragen.
;
; Fangen wir mit eine Basisklasse an. Ein abgeleitete Klasse (Vererbung) folgt später.
;     Wenn ich eine Basisklasse erstelle, lege ich immer alle Funktionen von den Interface Type 'IUnknown' an.
;     Somit kann, wenn man möchte, das Interface auch später für andere Sprachen exportieren.

DeclareModule BaseClass
  
  ; Beschreibung der Klasse
  Interface iBaseClass
    ; Anfang IUnknown
    QueryInterface(*riid, *addr)
    AddRef()
    Release()
    ; Ende IUnkwown
    SetText.s(Text.s)
    AddText.s(Text.s)
    GetText.s()
    LenOfText()
  EndInterface
  
  ; Strukture von der Klasse. Diese muss auch Public sein, damit die Vererbt werden kann
  Structure sBaseClass
    ; Basis
    *vTable     ; Zeiger auf die Funktionstabelle. Immer an erste Stelle
    cntRef.i    ; Anzahl wie oft das Objekt verwendet wird
    objMutex.i  ; Wird benötig wenn das angelegte Objekt von verschiedenen Thread verwendet wird
    ; Daten
    Text.s
    LenOfText.i
    ; ***
  EndStructure
  
  ; Definieren der Funktion zum anlegen eines Objektes aus der Klasse
  Declare New()
  
EndDeclareModule

Module BaseClass
  
  EnableExplicit
  
  ; Basis-Methoden anlegen (IUnknown)
  
  ; QueryInterface : Gehört zu IUnknown. wird noch nicht benötigt
  Procedure QueryInterface(*This.sBaseClass, *riid, *addr)
    ProcedureReturn $80004002 ; (#E_NOINTERFACE)
  EndProcedure
  
  ; ---------------------------------------------------------------------------
  
  ; AddRef : Damit wird die Anzahl der Verwendung des Objekt erhöht.
  Procedure AddRef(*This.sBaseClass)
    LockMutex(*This\objMutex)
    *This\cntRef + 1
    UnlockMutex(*This\objMutex)
    ProcedureReturn *This\cntRef
  EndProcedure
  
  ; ---------------------------------------------------------------------------
  
  ; Release : Damit wird der Speicher des Objekts wieder Freigegeben.
  ;           Aber nur wenn das Objekt nicht mehr verwendet wird. 
  Procedure Release(*This.sBaseClass)
    Protected index, cnt
    With *This
      LockMutex(\objMutex)
      If \cntRef = 0
        ; Weitere bearbeitung vor dem freigben des Objekts
        ; Zum Beispiel eine Funktion zum aufräumen aufrufen: 
        ; ---
        ; Dispose(*this)
        ; ---
        FreeMutex(*This\objMutex) ; Mutex wider freigeben
        FreeStructure(*This) ; Speicher wieder freigeben
        ProcedureReturn 0
      Else
        \cntRef - 1 ; Die Anzahl der Verwendung des Objekt reduzieren
      EndIf
      UnlockMutex(*This\objMutex)
      ProcedureReturn \cntRef
    EndWith
  EndProcedure
  
  ; ---------------------------------------------------------------------------
  
  ; Methoden anlegen
  Procedure.s SetText(*This.sBaseClass, Text.s) ; Ergebnis : Vorheriger Text
    Protected result.s
    With *This
      result = \Text
      \Text = Text
      \LenOfText = Len(Text)
    EndWith
    ProcedureReturn result
  EndProcedure
  
  Procedure.s AddText(*This.sBaseClass, Text.s) ; Ergebnis : Vorheriger Text
    Protected result.s
    With *This
      result = \Text
      \Text + Text
      \LenOfText = Len(Text)
    EndWith
    ProcedureReturn result
  EndProcedure
  
  Procedure.s GetText(*This.sBaseClass) ; Ergebnis : Aktueller Text
    Protected result.s
    With *This
      result = \Text
    EndWith
    ProcedureReturn result
  EndProcedure
  
  Procedure LenOfText(*This.sBaseClass, Text.s) ; Ergebnis : Länge vom Text
    Protected result.i
    With *This
      result = \LenOfText
    EndWith
    ProcedureReturn result
  EndProcedure
  
  ; Funktion zum anlegen eines Objektes
  
  Procedure New() ; Ergebnis : Zeiger auf das Objekt. Null wenn das Objekt nich angelgt werden konnte
    Protected *Object.sBaseClass
    ; Schritt 1: Speicher anfordern. Dazu bietet sich AllocateStructure an, weil diese auch den Speicher initialsiert
    *Object = AllocateStructure(sBaseClass)
    ; Schritt 2: Erforderlich Zeiger setzen und Mutex anlegen. Aber nur wenn speicher vorhanden ist (Out Of Memory ?)
    If *Object
      *Object\vTable = ?vtBaseClass     ; Zeiger auf die Funktionstabelle setzen (Methoden). Siehe weiter unten.
      *Object\objMutex = CreateMutex()  ; Mutex zum Schutz des RefCounter anlegen, kann auch in den Funktionen verwendet werden
                                        ; Zum Beipiel wenn auf das Objekt aus verschiedenen Threads zugegriffen wird
      *Object\cntRef = 0                ; 
      ; Eventuelle weitere Zuweisungen. Zum Beipiel eine New-Funktion mit Parametern
              
    EndIf
    ProcedureReturn *Object
  EndProcedure
  
  ; Tabelle für die Funktionen (Methoden) anlegen. Dazu bietet sich die DataSection gut an.
  ; Die Reihenfolge in der DataSection muss die gleiche sein wie die im Interface beschiebene Funktionen (Methoden)
  DataSection
    vtBaseClass: 
    Data.i @QueryInterface()
    Data.i @AddRef()
    Data.i @Release()
    Data.i @SetText()
    Data.i @AddText()
    Data.i @GetText()
    Data.i @LenOfText()
  EndDataSection
  
EndModule

;-Test

; Objekte anlegen und aufrufen. Die Objekte sind nicht als Struktur zu definieren, sondern als Interface.
; Am besten immer als Pointer definieren. Lässt sich besser lesen.
Define *Obj1.BaseClass::iBaseClass
Define *Obj2.BaseClass::iBaseClass
Define r1.s, r2.s, r3.s

*Obj1 = BaseClass::New()
*Obj2 = BaseClass::New()

*Obj1\SetText("Hallo ")
*Obj2\SetText("Welt")
r1 = *Obj1\GetText()
Debug r1
r2 = *Obj2\GetText()
Debug r2
*Obj1\AddText(*Obj2\GetText())
r3 = *Obj1\GetText()
Debug r3

; Objekte freigeben
c1 = *Obj1\Release()
Debug c1
c2 = *Obj2\Release()
Debug c2
Zuletzt geändert von mk-soft am 09.02.2018 21:23, insgesamt 4-mal geändert.
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
Benutzeravatar
mk-soft
Beiträge: 3691
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: Kleiner OOP Kurs mit Purebasic. Ohne Macros und PreCompi

Beitrag von mk-soft »

Abgeleitete Klasse (Vererbung)

Bei Vererbung ist das mit der virtuellen Tabelle in der DataSection kein Vorteil mehr. Dazu gibt es mehrere Ansätze diese zu lösen.
In meinen Beispiel für Vererbung habe ich die Methode gewählt für virtuelle Tabelle extra Speicher anzufordern (AllocateMemory).

Code: Alles auswählen

;- TOP

; OOP Programmierung unter Purebasic Part 2 - Vererbung und überschreiben einer geerbten Methode

; ***************************************************************************************

DeclareModule BaseClass
  
  ; Beschreibung der Klasse
  Interface iBaseClass
    ; Anfang IUnknown
    QueryInterface(*riid, *addr)
    AddRef()
    Release()
    ; Ende IUnkwown
    SetVorname(Text.s)
    SetNachname(Text.s)
    GetAll.s()
  EndInterface
  
  ; Strukture von der Klasse. Diese muss auch Public sein, damit die Vererbt werden kann
  Structure sBaseClass
    ; Basis
    *vTable     ; Zeiger auf die Funktionstabelle. Immer an erste Stelle
    cntRef.i    ; Anzahl wie oft das Objekt verwendet wird
    objMutex.i  ; Wird benötig wenn das angelegte Objekt von verschiedenen Thread verwendet wird
    ; Daten
    Vorname.s
    Nachname.s
    ; ***
  EndStructure
  
  ; Definieren der Funktion zum anlegen eines Objektes aus der Klasse
  Declare New()
  
  ; Hilfsfunktion für Vererbung. Für die Vererbung benötigen wir den Zugriff auf die Adresse der vituelle Tabelle der Klasse.
  Declare GetVT()
  
EndDeclareModule

Module BaseClass
  
  EnableExplicit
  
  ; Hilfsstruktur für den Zugriff auf die virtuelle Tabelle als ein Array Of Pointer
  Structure udtArrayVT
    *Addr[0] ; Mit der Angabe von Arraygröße '0' wird die Compilerüberwachung für den Index abgeschaltet
  EndStructure
  
  ; Pointer für Virutelle Tabelle
  Global *vtBaseClass.udtArrayVT
  
  ; Basis-Methoden anlegen (IUnknown)
  
  ; QueryInterface : Gehört zu IUnknown. wird noch nicht benötigt
  Procedure QueryInterface(*This.sBaseClass, *riid, *addr)
    ProcedureReturn $80004002 ; (#E_NOINTERFACE)
  EndProcedure
  
  ; ---------------------------------------------------------------------------
  
  ; AddRef : Damit wird die Anzahl der Verwendung des Objekt erhöht.
  Procedure AddRef(*This.sBaseClass)
    LockMutex(*This\objMutex)
    *This\cntRef + 1
    UnlockMutex(*This\objMutex)
    ProcedureReturn *This\cntRef
  EndProcedure
  
  ; ---------------------------------------------------------------------------
  
  ; Release : Damit wird der Speicher des Objekts wieder Freigegeben.
  ;           Aber nur wenn das Objekt nicht mehr verwendet wird. 
  Procedure Release(*This.sBaseClass)
    Protected index, cnt
    With *This
      LockMutex(\objMutex)
      If \cntRef = 0
        ; Weitere bearbeitung vor dem freigben des Objekts
        ; Zum Beispiel eine Funktion zum aufräumen aufrufen: 
        ; ---
        ; Dispose(*this)
        ; ---
        FreeMutex(*This\objMutex) ; Mutex wider freigeben
        FreeStructure(*This) ; Speicher wieder freigeben
        ProcedureReturn 0
      Else
        \cntRef - 1 ; Die Anzahl der Verwendung des Objekt reduzieren
      EndIf
      UnlockMutex(*This\objMutex)
      ProcedureReturn \cntRef
    EndWith
  EndProcedure
  
  ; ---------------------------------------------------------------------------
  
  ; Methoden anlegen
  
  Procedure SetVorname(*This.sBaseClass, Text.s)
    With *This
      \Vorname = Text
    EndWith
  EndProcedure
  
  Procedure SetNachname(*This.sBaseClass, Text.s)
    With *This
      \Nachname = Text
    EndWith
  EndProcedure
  
  Procedure.s GetAll(*This.sBaseClass)
    Protected result.s
    With *This
      result = \Nachname + "," + \Vorname
    EndWith
    ProcedureReturn result
  EndProcedure
  
  ; ---------------------------------------------------------------------------
  
  ; Funktion zum anlegen eines Objektes
  
  Procedure New() ; Ergebnis : Zeiger auf das Objekt. Null wenn das Objekt nich angelgt werden konnte
    Protected *Object.sBaseClass
    ; Schritt 1: Speicher anfordern. Dazu bietet sich AllocateStructure an, weil diese auch den Speicher initialsiert
    *Object = AllocateStructure(sBaseClass)
    ; Schritt 2: Erforderlich Zeiger setzen und Mutex anlegen. Aber nur wenn speicher vorhanden ist (Out Of Memory ?)
    If *Object
      *Object\vTable = *vtBaseClass     ; Zeiger auf die Funktionstabelle setzen (Methoden). Siehe weiter unten.
      *Object\objMutex = CreateMutex()  ; Mutex zum Schutz des RefCounter anlegen, kann auch in den Funktionen verwendet werden
                                        ; Zum Beipiel wenn auf das Objekt aus verschiedenen Threads zugegriffen wird
      *Object\cntRef = 0                ; 
      ; Eventuelle weitere Zuweisungen. Zum Beipiel eine New-Funktion mit Parametern
              
    EndIf
    ProcedureReturn *Object
  EndProcedure
  
  ; ---------------------------------------------------------------------------
  
  Procedure InitVT()
    ; Speicher für die virtuelle Tabelle anfordern.
    ; Die Compilerfunktion SizeOf gibt den benötigte Speichergröße von Interface in Bytes zurück
    *vtBaseClass = AllocateMemory(SizeOf(iBaseClass))
    
    ; Die Adressen der Funtionen zuweisen.
    ; Die Compilerfunktion OffsetOf gibt den Byte-Offset von der Interface-Methode zurück.
    ; Da wir den Index benötigen mussen wird diesen noch durch die Bytegröße vom Pointer teilen.
    ; Diesen erhalten über die Abfrage von der Bytegröße von Integer
    If *vtBaseClass
      With *vtBaseClass
        \addr[0] = @QueryInterface()
        \addr[1] = @AddRef()
        \addr[2] = @Release()
        \addr[OffsetOf(iBaseClass\SetVorname()) / SizeOf(integer)] = @SetVorname()
        \addr[OffsetOf(iBaseClass\SetNachname()) / SizeOf(integer)] = @SetNachname()
        \addr[OffsetOf(iBaseClass\GetAll()) / SizeOf(integer)] = @GetAll()
      EndWith  
    Else
      ; Out Of Memory!
      End 
    EndIf
  EndProcedure : InitVT() ; Und die Initalsierung aufrufen
  
  Procedure GetVT()
    ProcedureReturn *vtBaseClass
  EndProcedure
  
EndModule

; ***************************************************************************************

; Abgeleitete Klasse (Vererbung und überschreiben einer geerbten Methode)
;
;   Eine Struktur oder Interface kann man mit 'Extends' erweitern.
;   Dazu muss man wissen das die Erweiterung NICHT angehängt, sondern davor gesetzt wird. 
;   Das heißt das die geerbten Methode von der BaseClass vor den neuen Methoden stehen.

DeclareModule SubClass
  
  ; Beschreibung der neuen Klasse
  Interface iSubClass Extends BaseClass::iBaseClass ; Hier werden die Mehtoden der BaseClass davor gehängt
    SetStrasse(Text.s)
    SetPLZ(Nummer.i)
    SetOrt(Text.s)
  EndInterface
  
  ; Strukture von der neuen Klasse.
  Structure sSubClass Extends BaseClass::sBaseClass ; Hier wird die Stuktur der BaseClass übernommen.
                                                    ; Somit ist der erste Eintrag auch wieder die virtuelle Tabelle
    Strasse.s
    Plz.i
    Ort.s
  EndStructure
  
  ; Funktion zum anlegen eines Objektes
  Declare New()
  
EndDeclareModule

Module SubClass
  
  ; Hilfsstruktur für den Zugriff auf die virtuelle Tabelle als ein Array Of Pointer
  Structure udtArrayVT
    *Addr[0] ; Mit der Angabe von Arraygrösse '0' wird die Compilerüberwachung für den Index abgeschaltet
  EndStructure
  
  ; Pointer für Virutelle Tabelle
  Global *vtSubClass.udtArrayVT
  
  ; ---------------------------------------------------------------------------
  
  ; Die neuen Methoden anlegen
  Procedure SetStrasse(*This.sSubClass, Text.s)
    With *This
      \Strasse = Text
    EndWith
  EndProcedure
  
  Procedure SetPLZ(*This.sSubClass, Nummer.i)
    With *This
      \Plz = Nummer
    EndWith
  EndProcedure
  
  Procedure SetOrt(*This.sSubClass, Text.s)
    With *This
      \Ort = Text
    EndWith
  EndProcedure
  
  ; Hier schreiben wir die neue Methode die die alte ersetzen soll
  Procedure.s GetAll(*This.sSubClass)
    Protected result.s
    With *This
      result = \Nachname + "," + \Vorname + "," + \Strasse + "," + \Plz + " " + \Ort
    EndWith
    ProcedureReturn result
  EndProcedure
  
  ; ---------------------------------------------------------------------------
  
  ; Funktion zum anlegen eines Objektes
  
  Procedure New() ; Ergebnis : Zeiger auf das Objekt. Null wenn das Objekt nich angelgt werden konnte
    Protected *Object.sSubClass
    ; Schritt 1: Speicher anfordern. Dazu bietet sich AllocateStructure an, weil diese auch den Speicher initialsiert
    *Object = AllocateStructure(sSubClass)
    ; Schritt 2: Erforderlich Zeiger setzen und Mutex anlegen. Aber nur wenn speicher vorhanden ist (Out Of Memory ?)
    If *Object
      *Object\vTable = *vtSubClass      ; Zeiger auf die Funktionstabelle setzen (Methoden). Siehe weiter unten.
      *Object\objMutex = CreateMutex()  ; Mutex zum Schutz des RefCounter anlegen, kann auch in den Funktionen verwendet werden
                                        ; Zum Beipiel wenn auf das Objekt aus verschiedenen Threads zugegriffen wird
      *Object\cntRef = 0                ; 
      ; Eventuelle weitere Zuweisungen. Zum Beipiel eine New-Funktion mit Parametern
              
    EndIf
    ProcedureReturn *Object
  EndProcedure
  
  ; ---------------------------------------------------------------------------
  
  Procedure InitVT()
    ; Speicher für die virtuelle Tabelle der SubClass anfordern. Diese hat eine eigene neue Tabelle
    *vtSubClass = AllocateMemory(SizeOf(iSubClass))
    
    If *vtSubClass
      With *vtSubClass
        ; Als erstes benötigen wird die virtuelle Tabelle der geerbten Klasse.
        ; Dazu benutzen wir die Hilfsfunktion GetVT die uns den Zeiger auf die Tabelle zurückgibt
        ; und kopieren die Tabelle in unsere neuen Tabelle.
        CopyMemory(BaseClass::GetVT(), *vtSubClass, SizeOf(BaseClass::iBaseClass))
        
        ; Hier tragen wir die neuen Funktionen in die Tabelle ein
        \addr[OffsetOf(iSubClass\SetStrasse()) / SizeOf(integer)] = @SetStrasse()
        \addr[OffsetOf(iSubClass\SetPLZ()) / SizeOf(integer)] = @SetPLZ()
        \addr[OffsetOf(iSubClass\SetOrt()) / SizeOf(integer)] = @SetOrt()
        
        ; Nun ersetzen wir die Funktion der BaseClass mit der neuen Funktion der SubClass
        \addr[OffsetOf(iSubClass\GetAll()) / SizeOf(integer)] = @GetAll()
        
        ; Hinweis!
        ;   Sind beim freigeben des Objekt noch weitere arbeiten durch zuführen,
        ;   Muss auch die Methode Release() ersetzt werden.
        
      EndWith  
    Else
      ; Out Of Memory!
      End 
    EndIf
  EndProcedure : InitVT() ; Und die Initalsierung aufrufen
  
EndModule

; ***************************************************************************************

;-Test

; Objekte anlegen und aufrufen. Die Objekte sind nicht als Struktur zu definieren, sondern als Interface.
; Am besten immer als Pointer definieren. Lässt sich besser lesen.

Define *Obj1.BaseClass::iBaseClass
Define *Obj2.SubClass::iSubClass

*Obj1 = BaseClass::New()

*Obj1\SetVorname("Hans ")
*Obj1\SetNachname("Mustermann")
r1.s = *Obj1\GetAll()
Debug r1
; Objekte freigeben
c1 = *Obj1\Release()
Debug c1

; -----------------------------------

Define *Obj2.SubClass::iSubClass

*Obj2 = SubClass::New()

*Obj2\SetVorname("Hans ")
*Obj2\SetNachname("Mustermann")
*Obj2\SetStrasse("Lindenstrasse 2")
*Obj2\SetPLZ(20100)
*Obj2\SetOrt("Hamburg")
r1.s = *Obj2\GetAll()

Debug r1
; Objekte freigeben
c1 = *Obj2\Release()
Debug c1
Zuletzt geändert von mk-soft am 10.02.2018 22:14, insgesamt 3-mal geändert.
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
Benutzeravatar
Kurzer
Beiträge: 1614
Registriert: 25.04.2006 17:29
Wohnort: Nähe Hamburg

Re: Kleiner OOP Kurs mit Purebasic. Ohne Macros und PreCompi

Beitrag von Kurzer »

:allright: Danke für den Blick hinter die Kulissen deiner BaseClass bzw. den Erklärungen dazu.

Möglicherweise verstehe ich jetzt auch den Interface Befehl besser. Ist es so, dass bei allen objektbasierten externen Funktionen (DLL z.B.) durch das Erstellen eines Objekts ein Zeiger auf die Objektstruktur zurückgegben wird und sich an erster Stelle immer die Adresse der Funktionstabelle befindet?

Das Beispiel aus der Hilfe war mir bisher ohne dieses Wissen doch etwas unverständlich - zumindest vom internen Aufbau, da dort ja de Funktionsweise von 'MyCreateObject()' nicht erklärt wird.
PurBasic Hilfe hat geschrieben:

Code: Alles auswählen

  ; Um auf ein externes Objekt (in einer DLL zum Beispiel) zugreifen zu
  ; können, muss zuerst das Objekt-Interface deklariert werden:
  ;
  Interface MyObject
    Move(x,y)
    MoveF(x.f,y.f)
    Destroy()
  EndInterface
  
  ; CreateObject ist die Funktion, welche das Objekt (aus der DLL) erstellt,
  ; dessen Interface gerade definiert wurde.
  ; Erstelle das erste Objekt...
  ;
  Object1.MyObject = MyCreateObject()
Mich kannst Du auf jeden Fall als festen Leser hier einplanen. Ich nutze Deine Klasse ja immerhin als "BlackBox" fleißig. ;-)
"Never run a changing system!" | "Unterhalten sich zwei Alleinunterhalter... Paradox, oder?"
PB 6.02 x64, OS: Win 7 Pro x64 & Win 11 x64, Desktopscaling: 125%, CPU: I7 6500, RAM: 16 GB, GPU: Intel Graphics HD 520
Useralter in 2023: 56 Jahre.
Benutzeravatar
Josh
Beiträge: 1028
Registriert: 04.08.2009 17:24

Re: Kleiner OOP Kurs mit Purebasic. Ohne Macros und PreCompi

Beitrag von Josh »

Was ich noch drin hab bei meinen Objekten (und das ist sogar einer der Hauptgründe warum ich den ganzen Zauber überhaupt mache) ist ein Verweis auf das Parentobjekt. Der Konstruktor trägt das Parentobjekt in die Objektdaten und zusätzlich in eine Objektliste ein.

Ein neues Objekt wird dann wie folgt erstellt:

Code: Alles auswählen

New_XYZ (*Parent) 
Das erste Objekt, dass alle anderen Objekte enthält sieht dann wie folgt aus:

Code: Alles auswählen

*Me = New_App (0)
Wenn nun ein Objekt gelöscht wird (egal ob das oberste oder ein Nachkomme) passiert folgendes:

Das erste was der Destruktor macht, er sieht in der Objektlist nach, ob das eigene Objekt Childs hat. Diese Childobjekte werden dann durchlaufen und der Destruktor der Childs aufgerufen. Beim löschen dieser Childobjekte passiert dann natürlich das gleiche, dass im Destrukor als erst Aktion geprüft wird, ob dieses Childobjekt selbst Childs hat. Die letzten Aktionen im Destroktor sind dann das löschen des Objektes aus der Objektliste und löschen des Objektes selbst.

Somit werden dann mit einem Löschbefehl aus dem Hauptprogramm, das Objekt und alle Child/ChildChild/...objekte hierarchisch von unten nach oben gelöscht und ich habe keine Gefahr, dass ich etwas vergessen kann.

Mit einem einfachen

Code: Alles auswählen

*Me\Destroy()
wird die App beendet und es wird alles in umgekehrter Reihenfolge wie es erstellt wurde wieder freigegeben, gelöscht oder geschlossen.
Benutzeravatar
mk-soft
Beiträge: 3691
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: Kleiner OOP Kurs mit Purebasic. Ohne Macros und PreCompi

Beitrag von mk-soft »

@Kurzer
Da stimmt. Ein Objekt hat immer als ersten Eintrag den Zeiger auf die Funktionstabelle.
Die Hilfe von Purebasic ist im Bezug auf Interface leider absolute unzureichend.

@Josh
Da sieht man es. Die Methode Parent muss das Objekt liefern. Oder anders ausgedrückt bei OOP! "Der Elefant hat die Methode Fangen mitzuliefern!"


Zur Erläuterung von Parent:
Ein Parent Objekt kann es nur geben wenn eine Methode nicht ein Wert, sondern ein Objekt zurückgibt.
Somit muss beim anlegen des Objektes der Verweis auf das Parent Objekt mit übergeben werden.

Es ist aber nicht die Regel wenn das Parent Objekt freigeben wird, auch das angelegte Objekt mit freigeben werden muss.
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
Benutzeravatar
Josh
Beiträge: 1028
Registriert: 04.08.2009 17:24

Re: Kleiner OOP Kurs mit Purebasic. Ohne Macros und PreCompi

Beitrag von Josh »

mk-soft hat geschrieben:Die Methode Parent muss das Objekt liefern.
Parent ist bei mir keine Methode, ist nur ein Eintrag im Objekt. Wichtiger als der Parent-Eintrag ist jedoch die List der Childobjekte im Parentobjekt, sonst kannst du ja keinen Bezug auf untergeordnete Objekte herstellen.
mk-soft hat geschrieben:Es ist aber nicht die Regel wenn das Parent Objekt freigeben wird, auch das angelegte Objekt mit freigeben werden muss.
Bei mir hat jedes Objekt (außer das App-Root-Objekt) ein Parent-Objekt und wird mit dem Parent-Objekt automatisch freigegeben. Somit gibt es keine frei im Raum herumschwebende Objekte. Ich habe noch nie einen Fall gehabt, wo ein Objekt noch bestehen sollte, wenn es das Parent-Objekt nicht mehr gibt.

Ein Objekt hat zumindest das App-Root-Objekt als Parent, wird dadurch automatisch freigegeben, wenn die App beendet wird. Aber da gibt es wahrscheinlich tausende von Möglichkeiten, wie jeder sein Ziel erreicht.
Benutzeravatar
mk-soft
Beiträge: 3691
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: Kleiner OOP Kurs mit Purebasic. Ohne Macros und PreCompi

Beitrag von mk-soft »

Zweiter Beitrag ist vorläufig fertig. :wink:

Ich glaube da muss noch einer wegen der Rechtschreibung rüber gucken. 8)

Vererbung und überschreiben von Methoden.

Siehe zweiten Eintrag
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
Benutzeravatar
Kurzer
Beiträge: 1614
Registriert: 25.04.2006 17:29
Wohnort: Nähe Hamburg

Re: Kleiner OOP Kurs mit Purebasic. Ohne Macros und PreCompi

Beitrag von Kurzer »

Das stimmt, so viel Informationen, aber spannend und interessant allemal. Danke. :allright:
"Never run a changing system!" | "Unterhalten sich zwei Alleinunterhalter... Paradox, oder?"
PB 6.02 x64, OS: Win 7 Pro x64 & Win 11 x64, Desktopscaling: 125%, CPU: I7 6500, RAM: 16 GB, GPU: Intel Graphics HD 520
Useralter in 2023: 56 Jahre.
Antworten