NicTheQuick hat geschrieben:Ja. Nenn es einen Nachteil, aber ich finde es nicht schlimm, wenn alle Member-Variablen automatisch private sind, solange man nicht superm() benutzt oder einen Getter dafür schreibt.
Nenn es Charakter
Ich hatte mich nur gewundert, wie du es mit Super hingebracht hast und das erklärts.
Mir war es egal, ob man das 'this' bei jeder Methode noch hinschreibt oder nicht. Bei einigen Programmiersprachen ist das sowieso standard. Siehe z.B. Python.
Mir geht es auch etwas um die Überprüfung. Ich möchte halt so viele Probleme wie möglich abfangen.
Ich hab bei mir jetzt ein
Code: Alles auswählen
CompilerIf Not Defined(*self,#PB_Variable)
CompilerError "Missing 'This'!"
CompilerEndIf
beim Method-Macro eingefügt. *self wird mittels "This" in Parameterblock definiert. Fehlt es, läuft was schief -> Fehlermeldung beim compilen. Zwar kann ich die Position von This damit nicht überprüfen, aber immerhin ob es vorhanden ist.
Anders wollte es irgendwie nicht. Hauptsache es geht.

Imo ein Bug in PB. Übrigens: Das Leerzeichen vor den Doppelpunkt ist auch wichtig!
Ich hab jetzt meine Variante hier hochgeladen:
http://game.gpihome.eu/PureBasic/oop.7z
Mittlerweile bin ich recht flexibel, was die Klassenerstellung angeht:
Code: Alles auswählen
DeclareClass (cChild [,cParent] )
Get()
Set()
Properties
value.i
object.cIrgendwas
EndDeclareClass
[...]
Class (cChild [,options])
InitalizeObject(object, cIrgendwas [,Arraysize] )
Method (i, Initalize, (this,*initvalue) ); Konstruktor - ein Initalwert ist möglich
MethodReturn (xxx)
EndMethod
[....]
EndClass
oder
Code: Alles auswählen
DeclareClass (cChild [,cParent] )
Get()
Set()
EndDeclareClass
[...]
Class (cChild [,options])
value.i
object.cIrgendwas
InitalizeObject(object, cIrgendwas [,Arraysize] )
Method (i, Initalize, (this,*initvalue) ); Konstruktor - ein Initalwert ist möglich
MethodReturn (xxx)
EndMethod
[....]
EndClass
oder
Code: Alles auswählen
Interface cChild [ extends cParent]
Get()
Set()
EndInterface
InitalizeClass(cChild [,cParent] )
[...]
Class (cChild [,options])
value.i
object.cIrgendwas
InitalizeObject(object, cIrgendwas [,Arraysize] )
Method (i, Initalize, (this,*initvalue) ); Konstruktor - ein Initalwert ist möglich
MethodReturn (xxx)
EndMethod
[....]
EndClass
oder
Code: Alles auswählen
Interface cChild [ extends cParent]
Get()
Set()
EndInterface
InitalizeClassEx(cChild [,cParent] )
value.i
object.cIrgendwas
EndInitalizeClassEx
[...]
Class (cChild [,options])
InitalizeObject(object, cIrgendwas [,Arraysize] )
Method (i, Initalize, (this,*initvalue) ); Konstruktor - ein Initalwert ist möglich
MethodReturn (xxx)
EndMethod
[....]
EndClass
Warum? Wenn man die Autovervollständigung nutzen will, dann kommt man um Interface-Endinterface nicht drumherum. Das sieht zwar nicht mehr ganz so schön im Code aus, aber es funktioniert.
Normalerweise würde ich sagen, das die Member-Variable-Definition eigentlich zur Klassendefinition gehört. Das funktioniert auch normalerweise wunderbar, es gibt aber eine große Ausnahme: Module (dazu gleich).
Folgende Class-Options gibt es:
oop::#AutoMutex - Jede Methode der Klasse wird automatisch mittels Mutex umschlossen
oop::#NoCreation - Verbietet die Benutzung der Klasse. Ist für Dummy-Klassen gedacht, die man nur erstellt, damit man sie als Parent nutzt.
oop::#NoChild - Die Klasse kann nicht als Parent benutzt werden. Wozu das gut sein soll? Keine Ahnung, aber ich hab mir überlegt, was man alles als Option nutzen kann
Der große Vorteil meines Codes ist, das es Modul-unabhängig ist. Man kann Klassen vollständig in Module-EndModule einfügen und die Klasse ist dann vollständig drin versteckt. Man kann auch die Klassen in Declare-Bereich declarieren und in "Rumpf" dann definieren. Eine Benutzung von außerhalb ist dann auch möglich mit modulename::klasse. Es gibt nur ein Problem: Eine solche Klasse kann nur ein als Parent dienen, wenn auch die Variablen in Declarations-Bereich sind. Darum die ganzen Varianten bei der Erstellung.
Mit Define_Object, Global_Object, Static_Object, Protected_Object werden Objecte erzeugt, die automatisch bei _ProcedureReturn, _EndProcedure und _End zerstört werden. Neu ist, das man einen InitalWert (integer oder halt Pointer) mitgeben kann. Mehr sind leider mit meiner Methode, wie ich die Klassen verwalte, nicht möglich. Notfalls kann man ja einen Pointer auf eine Struktur mitgeben. Meine Methode hier ist, das ich einen Default-Konstruktor habe, der immer aufgerufen wird und unabhängig von der Klasse ist. Der Ruft dann auch die Klassen-Initalize-Methode auf, sofern definiert.
Das "new" heißt bei mir AllocateObject() - damit wird ein freies Objekt erzeugt, das man selber mit FreeObject() wieder freigeben muss. CloneObject ist ähnlich, nur wird eine 1:1-Kopie des Objects erstellt (oder Null, falls es fehlschlägt).
Ansonsten gibts noch das CheckClass, CopyObject, ResetObject. Name sollte Programm sein.
Ich hab viel Wert auf Debug-Möglichkeiten gelegt. So werden, wenn der Debugger läuft, sämtliche Objekte in einer Liste erfasst. Bei _End werden dann alle nicht korrekt freigegeben Objekte aufgelistet und wo sie erzeugt werden. Zudem kann man mit DebugObject eine Meldung ausgeben, die auch die Objekterstellungsposition enthält. Bei der Klassenerstellung wird zudem kontrolliert, ob wirklich zu jeder Methode auch definiert wurde. Das passiert leider erst zur Laufzeit.
Das ganze wird aber nur mit eingeschalteten Debugger integriert, ansonsten ist es zugunsten der Geschwindigkeit und Speichernutzung ausge-compilerif-ed.