amiga-news ENGLISH VERSION
.
Links| Forum| Kommentare| News melden
.
Chat| Umfragen| Newsticker| Archiv
.

amiga-news.de Forum > Programmierung > wie programmiert man "vererbung" [ - Suche - Neue Beiträge - Registrieren - Login - ]

-1- 2 [ - Beitrag schreiben - ]

18.12.2010, 17:37 Uhr

AGSzabo
Posts: 1663
Nutzer
ja hallo erstmal,

ich habe hier bis vor ein paar monaten viele fragen im bezug auf ein selbstgestricktes "objekt orientiertes" gui-system gestellt. das hat sich seit dem auch doll entwickelt:

http://images.quicktunnels.net/xuibig.jpg

ich habe klassen, methoden und attribute, aber nun dämmert mir, dass ich evtl etwas grundlegendes falsch verstanden habe, denn offenbar können bei mir klassen nicht wirklich voneinander erben!

zum beispiel wüsste ich nicht, wie man es im konzept haben muss, damit man einen button um ein image im button erweitern kann. es ist bei mir zwar möglich, das image dem button als member anzufügen, aber es lassen sich nicht beide als eine einheit ansprechen und der button kann auch nicht die horizontal-gruppen-klasse zur positionierung des images neben dem text verwenden. dazu müsste man nach meinem aktuellen konzept eine kontrollschicht über die bestandteile bauen ... falls ihr wisst was ich meine. :-)

daher die ertmal nur rein theoretische frage: "Wie programmiert man Vererbung?", zB anhand des beipiels mit dem button. ich merke an, dass ich mit 68k assembler programmiere. :-)

ags
--
Sam mini os4.1 upd. 2 / e-uae 39bb2 / A4000D 3.0 & 3.9 2mbchip 8mbfast Ariadne_II ide DVD und HD / A500 3.1 (mkick) adide 50mb / Athlon ii X2 Ubuntu Linux

[ Dieser Beitrag wurde von AGSzabo am 18.12.2010 um 20:01 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

18.12.2010, 21:34 Uhr

Der_Wanderer
Posts: 1229
Nutzer
Vielleicht hilft dir das hier weiter:

http://hd-rec.de/A/inherit.html

oder schau dir den Source von NTUI an ;-)

Ansonsten:

Du machst eine generelle Klasse "Button".

Die hat die Felder und die Funktion


code:
struct Button {
  int x,y;
  int width, height;
  int borderstyle;
}

void Button_Draw(Button *btn, RastPort *rp);
void Button_SetSize(Button *btn, int Width, int Height);


Draw zeichnet, was bei allen Buttons gleich ist, z.b. die Border und den Hintergrund.

Jetzt machst du eine Subklasse "ImageButton", die bekommt zusätzlich das Feld "Bitmap" und muss die Funktion Draw ersetzen bzw. ergänzen:

code:
struct ImageButton {
  struct Button;
  Bitmap *bmap;
}

void ImageButton_Draw(*ImageButton *btn, RastPort *rp) {
  Button_Draw(btn, rp);
  BltBmapRastPort(rp,btn->Bitmap);
}


Die Funktion Draw wurde also überladen. Die Funktion SetSize muss nicht überschrieben werden, wurde also geerbt, genauso wie die Felder vom Button.

Wenn man keine Sprache hat, die OOP unterstützt, muss man dafür Funktionpointer nehmen, sonst ist kein überladen möglich.

--
--
Author of
HD-Rec, Sweeper, Samplemanager, ArTKanoid, Monkeyscript, Toadies, AsteroidsTR, TuiTED, PosTED, TKPlayer, AudioConverter, ScreenCam, PerlinFX, MapEdit, AB3 Includes und viele mehr...
Homepage: http://www.hd-rec.de


[ - Antworten - Zitieren - Direktlink - ]

18.12.2010, 21:54 Uhr

AGSzabo
Posts: 1663
Nutzer
Zitat:
Original von Der_Wanderer:
Wenn man keine Sprache hat, die OOP unterstützt, muss man dafür Funktionpointer nehmen, sonst ist kein überladen möglich.

Kannst du das detaillierter erklären bitte? Meine Methoden funktionieren akteulle so, dass es pro klasse einen dispatcher gibt, der die ID der Methode mit definierten werten vergleicht und davon abhängig in die funktionen verzweigt. der dispatcher versteht jeweils nur methoden seiner klasse (unten oxYXM_), da die methoden einer anderen klasse wieder die selben werte benutzen. ausnahme: zentrale methoden die JEDE klasse unterstützen kann (unten OXM_).

code:
.dispatcher	cmp.w	#OXM_SET,d1
		beq	PM_SET

		cmp.w	#OXM_GET,d1
		beq	PM_GET

		cmp.w	#OXM_SETDEF,d1
		beq	PM_SETDEF

		cmp.w	#OXM_INIT,d1
		beq	PM_INIT

		cmp.w	#OXM_DEINIT,d1
		beq	PM_DEINIT

		cmp.w	#oxYXM_popup,d1
		beq	PM_popup

		rts


In "_SET" zB werden dann die Attribute anhand einer ID mit werten verglichen. Jede Klasse versteht nur ihre eigenen Attribute weil alle Klassen die selben Werte benutzen. Ausnahme: Attribute welche jede klasse kennen kann.

Soll bei mir zB ein button ein image unterstützen, müsste man ein meta-objekt über beide drüber machen, das Attribute und Methoden unter ihm eigenen namen empfängt und an die Komponenten übersetzt weiterleitet...

--
Sam mini os4.1 upd. 2 / e-uae 39bb2 / A4000D 3.0 & 3.9 2mbchip 8mbfast Ariadne_II ide DVD und HD / A500 3.1 (mkick) adide 50mb / Athlon ii X2 Ubuntu Linux

[ Dieser Beitrag wurde von AGSzabo am 18.12.2010 um 22:14 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

18.12.2010, 23:10 Uhr

thomas
Posts: 7716
Nutzer
@AGSzabo:

Vererbung funktioniert nur, wenn zwischen den Klassen eine Mutter-Kind-Beziehung besteht. Wie im Beispiel oben, da ist die Button-Klasse die Mutter und die ImageButton-Klasse das Kind. Die Kindklasse erbt alle Methoden der Mutterklasse, die sie nicht selbst implementiert. Programmieren kannst du das ganz einfach, indem du im Dispatcher der Kindklasse alle unbekannten Methoden an den Dispatcher der Mutterklasse weitergibst.

Natürlich musst du deine Datenstruktur auch entsprechend arrangieren. So wie im Beispiel oben, dass die Datenfelder der Kindlasse immer direkt hinter den Daten der Mutterklasse liegen. Dadurch kannst du beim Durchreichen der Methoden immer den gleichen Objektpointer weitergeben und der Dispatcher weiss aufgrund der Klasse zu der er gehört, an welchem Offset seine speziellen Datenfelder beginnen.

Gruß Thomas

--
Email: thomas-rapp@web.de
Home: thomas-rapp.homepage.t-online.de/

[ - Antworten - Zitieren - Direktlink - ]

18.12.2010, 23:31 Uhr

AGSzabo
Posts: 1663
Nutzer
@thomas:

Das setzt vorraus, dass

- die unbekannten methoden und attribute ihre IDs in einem anderen werteraum haben als wie die bekannten
- die kindklasse die strukturgröße der mutterklasse kennt (?)

bei mir sind die strukturgrößen aller klassen geheim und die methoden IDs und attribute IDs sind nicht einzigartig. durch letzteres spare ich mir eine zentrale registrierung für klassen-autoren ...
--
Sam mini os4.1 upd. 2 / e-uae 39bb2 / A4000D 3.0 & 3.9 2mbchip 8mbfast Ariadne_II ide DVD und HD / A500 3.1 (mkick) adide 50mb / Athlon ii X2 Ubuntu Linux

[ - Antworten - Zitieren - Direktlink - ]

19.12.2010, 09:42 Uhr

thomas
Posts: 7716
Nutzer
@AGSzabo:

Das ist genau das, was eine Mutter-Kind-Beziehung ausmacht. Die ist für Vererbung notwendig. Bei dir sind alle Klassen einzeln und unabhängig voneinander. Bei einer Mutter-Kind-Beziehung ist das Kind abhängig von der Mutter. Die Kindklasse muß den Dispatcher und die Datengröße der Mutterklasse kennen. Eine Kindklasse kann auch nur Kind von genau einer Mutterklasse sein. Du kannst ein Kind nicht je nachdem, was du gerade brauchst, an die eine oder die andere Mutter dranhängen.

Vererbung heißt, dass die Kindklasse alle Methoden und Attribute (und deren IDs) von der Mutterklasse erbt und nur einzelne hinzufügt oder ersetzt.

--
Email: thomas-rapp@web.de
Home: thomas-rapp.homepage.t-online.de/

[ - Antworten - Zitieren - Direktlink - ]

19.12.2010, 12:08 Uhr

Kronos
Posts: 1168
Nutzer
Holzhammermethode (nur zu Veranschauung):

Jedes "Picture_button"-Objekt enthält ein "Button"-Objekt. Trifft der Picture-Button-Dispatcher nun auf eine Methode die er nicht kennt ruft er einfach den Dispatcher der Button-Klasse auf (so macht das im Prinzip auch MUI).

Prinzipliell sollten IDs für Methoden und Attribute immer nur 1mal genutzt werden (was aber kein Problem sein sollte bei gut 4 Millarden Möglichkeiten pro ULONG).

MfG
Kronos
--
Only the good die young all the evil seem to live forever

[ - Antworten - Zitieren - Direktlink - ]

19.12.2010, 21:05 Uhr

Der_Wanderer
Posts: 1229
Nutzer
@AGSzabo
Das was du machst ist nur ansatzweise objektorientiert.

So eine switch-Kaskade ist ineffizient (gutes Beispiel, dass Assembler nicht durch seine bloße Verwendung zu schnellerem Code führt).

Das Problem dabei ist, dass alles statisch ist, d.h. alle Subklassen müssen zur Compilezeit bekannt sein.

Das macht es aber unmöglich, dein GUI system durch 3rd Party widgets zu erweitern, oder dass der Programmierer eine Variente mittels subclassing erstellen kann, z.b. einen speziellen Scroller der Suchtreffer farblich anzeigt.

Deshalb benötigst du Function-Pointer.

Also z.b. nochmal zum Button:

code:
struct Button {
  int x,y;
  int width, height;
  int borderstyle;
  Hook *Draw;
  Hook *SetSize;
}

Button* Button_Create() {
  btn = malloc();
  btn->x = 0,0;
  btn->Width = 100,20;
  btn->BorderStyle = 1;
  btn->Draw = &Button_Draw();
  btn->SetSize = &Button_SetSize();
  return btn;
}

void Button_Draw(Button *btn, RastPort *rp);
void Button_SetSize(Button *btn, int Width, int Height);


Eigentlich muss die Button Struct so aussehen.
Wenn ich nun den Image Button subclasse, dann kommt folgendes dazu:

code:
struct ImageButton {
  struct Button btn;
  Bitmap *bmap;
  Hook *SetBitmap;
}

ImageButton* ImageButton_Create() {
  btn = Button_Create();
  btn->bmap = ...
  btn->SetBitmap = &ImageButton_SetBitmap();
  btn->btn.Draw  = &ImageButton_Draw();

}

void ImageButton_Draw(ImageButton *btn, RastPort *rp) {
  Button_Draw(btn, rp);
  BltBmapRastPort(rp,btn->Bitmap);
}

void ImageButton_SetBitmap(*imageButton *btn, char* filename) {
  ...
}


Also, was ist hier passiert:

Button ist bereits ein eigentständiges Widget.
Dann kommt der ImageButton, dessen erster Teil der Struct dem Button entspricht. Somit kann man alle Operationen durchführen, die der Button schon kann, ohne dass man was doppelt implementieren muss.
Z.b. mit SetSize die Dimensionen setzen.
Dann bekommst der ImageButton aber nach das Feld "Bitmap", um das Bild zu speichern, und einen eigenen Konstruktor, der das initialisiert. (der Vollständigkeit halber benötigt man natürlich auch noch einen Destruktor).
Dann gibt es eine neue Methode "SetBitmap", die nur die Subklasse beherrscht. Auf einem Button Objekt kann man die nicht aufrufen.

Dann gibt es noch Draw, was Überladen wird mit einer modifizierten Zeichenroutine, die das Bild zusätzlich malt.

Das tolle am Überladen ist nun, dass man z.b. eine Liste von Buttons haben kann, wobei man nicht wissen muss welche Subklasse das nun genau ist. Man kann alle durchgehen und "btn->Draw" aufrufen, und alle malen sich korrekt.

Meiner Erfahrung nach mit NTUI würde ich nicht tiefer Subklassen als 3 Stufen.

Also z.b. du hast die Oberklasse "Widget".
Da sind solche Sachen drin wie x/y, width/height, borderstyle, visibility, bgColor, fgColor, Font, ID und die Methoden "Draw", "CalculateMinSize", "Layout", "DispatchEvent", "Free".

Eben alles, was alle Widgets gemeinsam haben, evtl. ein bisschen mehr.
Dann gibt es die Unterklassen, die die einzelnen Widget Typen implementieren, also Button, String, Cycle, ListView, Scroller, Slider, etc.
Darunter gibt es dann, sofern nötig, nochmal Unterklassen wie z.b.
Widget->String->NumString
Widget->String->MultiLine
Widget->Button->ImageButton
Widget->Button->Toggle
Widget->Button->Textbutton
Widget->Button->CheckBox

etc. etc.

Mehr Tiefen kann ich nicht empfehlen, dann wird es unübersichtlich.


--
--
Author of
HD-Rec, Sweeper, Samplemanager, ArTKanoid, Monkeyscript, Toadies, AsteroidsTR, TuiTED, PosTED, TKPlayer, AudioConverter, ScreenCam, PerlinFX, MapEdit, AB3 Includes und viele mehr...
Homepage: http://www.hd-rec.de



[ Dieser Beitrag wurde von Der_Wanderer am 19.12.2010 um 21:09 Uhr geändert. ]

[ Dieser Beitrag wurde von Der_Wanderer am 19.12.2010 um 21:16 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

19.12.2010, 21:14 Uhr

AGSzabo
Posts: 1663
Nutzer
@Der_Wanderer:

so wie ich das sehe sind da also dann die pointer für die funktionen wie zb Draw() nicht im dispatcher der klasse, sondern es sind felder in der zB Button-Struktur?
--
Sam mini os4.1 upd. 2 / e-uae 39bb2 / A4000D 3.0 & 3.9 2mbchip 8mbfast Ariadne_II ide DVD und HD / A500 3.1 (mkick) adide 50mb / Athlon ii X2 Ubuntu Linux

[ - Antworten - Zitieren - Direktlink - ]

19.12.2010, 21:19 Uhr

Der_Wanderer
Posts: 1229
Nutzer
Richtig. Und wenn du auch dem Benutzer erlaubst, eigene Funktionen reinzuhängen (unter Zugriff auf die Original), dann ist dein System offen und viel mächtiger. Du kannst nämlich nicht alle Wünsche der Programmierer befriedigen. Das ist ein Fass ohne Boden.

Einen Dispatcher im dem Sinn gibt es nicht.
Einen Dispatcher gibt es für Events, die verarbeitet werden. Die treten aber sehr viel seltener auf als Methoden, deshalb ist das wichtig dass die Methoden effizient aufgerufen werden, z.b. wenn eine komplexere GUI resized wird, oder wenn viele Attribute gesetzt werden.

Der Vorteil von Funktion Pointern ist eben die Erweiterbarkeit, und der Aufwand ist O(1), während dein Dispatcher einen Aufwand von O(n) hat.

Wo man eine Art Dispatcher einbauen kann sind die Getters und Setters.

Also

GetAttr(widget, attr, value);
value = GetAttr(widget, attr);

Da man nicht unbedingt für jedes spezielle Attribut eine eigene Funktion braucht. Ich würde die Funktionen eher gering halten, und
Widgets nur über Get/SetAttr verändern. Dann ist alles zentral unter Kontrolle.
Man kann dann eine "ConvinientStubbs" Biliothek machen, wo dann sowas gewrapped wird:

SetTextForStringGadget(StringButton* btn, char *text) {
btn->SetAttr(btn, STRINGA_TEXT, text);
}

Aber das sollte man seperat halten. Es sollte klar sein, was eine reine Convinient Stubb ist und was eine echte Funktion ist.

--
--
Author of
HD-Rec, Sweeper, Samplemanager, ArTKanoid, Monkeyscript, Toadies, AsteroidsTR, TuiTED, PosTED, TKPlayer, AudioConverter, ScreenCam, PerlinFX, MapEdit, AB3 Includes und viele mehr...
Homepage: http://www.hd-rec.de



[ Dieser Beitrag wurde von Der_Wanderer am 19.12.2010 um 21:31 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

19.12.2010, 21:26 Uhr

AGSzabo
Posts: 1663
Nutzer
@Der_Wanderer:

Mir ist nicht klar wie nun genau die struktur aus sieht, die beide klassen in einer instanz zusammenfasst. ich hätte es bei mir aktuell so, daß jedes objekt einen generellen objekt-header hat. da steht zB der zeiger auf die klasse drin sowie ein paar flags die jedes objekt hat. danach kommen die klassenspezifischen felder. wenn nun so ein objekt als mutter hergenommen werden soll, kämen dann die felder des kind-objekts direkt nach denen des mutter objekts? ich muss doch doch beim kompilieren der kind klasse die size der mutterklasse kennen!

und wie wandle ich eine methoden id in einen funktionpointer um und wie mache ich das wohl änliche spiel mit den attributen?
--
Sam mini os4.1 upd. 2 / e-uae 39bb2 / A4000D 3.0 & 3.9 2mbchip 8mbfast Ariadne_II ide DVD und HD / A500 3.1 (mkick) adide 50mb / Athlon ii X2 Ubuntu Linux

[ Dieser Beitrag wurde von AGSzabo am 19.12.2010 um 21:28 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

19.12.2010, 21:55 Uhr

CyberZorro
Posts: 47
Nutzer
OOP for Dummies
--
A600/000/6MB/HD | WinUAE

[ - Antworten - Zitieren - Direktlink - ]

21.12.2010, 16:22 Uhr

Holger
Posts: 8116
Nutzer
Zitat:
Original von AGSzabo:
wenn nun so ein objekt als mutter hergenommen werden soll, kämen dann die felder des kind-objekts direkt nach denen des mutter objekts? ich muss doch doch beim kompilieren der kind klasse die size der mutterklasse kennen!

Nein, musst Du nicht. Das einzige, was Du, zumindest bei traditionellen OOP-Umsetzungen, kennen musst, ist die Elternklasse. Wobei nicht einmal das zur Compile-Zeit feststehen muss.

Wenn nun eine Funktion, bzw. Methode Daten aus dem Objekt braucht, fragt es zuerst seine Elternklasse nach seinem Offset innerhalb des Objekts, welches letztendlich die Summe der Größen aller Elternklassen ist. Alle eigenen Daten sind relativ zu diesem Offset. Wird diese Klasse selbst von einer Unterklasse nach einem Offset gefragt, reicht es die Anfrage an die Superklasse weiter, addiert dann seine eigene Größe zu dem Ergebnis und gibt das an die Unterklasse zurück.

Übrigens ist eine "ImageButton"-Klasse als Unterklasse einer "Button"-Klasse schon ein Irrweg. Es reicht vollkommen, wenn man eine "Button"-Klasse hat, die sich wie ein Button verhält. Diese hat irgendeine Form der Darstellung als Eigenschaft, also Attribut. Und diese Darstellung kann unterschiedliche Unterklassen wie Text oder Image oder Zusammengesetzt besitzen. Dann kannst Du diese Formen der Darstellung auch für Label oder Menüpunkte oder was auch immer wiederverwenden, statt für jede dieser Klassen eine ImageIrgendetwas Unterklasse bilden zu müssen.

--
Good coders do not comment. What was hard to write should be hard to read too.

[ - Antworten - Zitieren - Direktlink - ]

22.12.2010, 08:35 Uhr

AGSzabo
Posts: 1663
Nutzer
@Holger:

Ok, das mit dem Fragen nach dem Offset ist genialst! Mir ist noch nicht klar wo im ganzen Objekt die zeiger sind auf meine Unterklasse und Elternklasse. Und woher die Unterklasse weis dass sie nur ein Teil eines Größeren ist. Und dann brauch ich für die Unterklasse nur noch deren ihre Datenfelder, aber keine volle Objekt-Struktur mit meinem allg. Objekt-Header mehr?

ps: ich habe mir grad überlegt, es könnte ja die subklasse einen pointer auf die elternklasse haben und so von da den offset auslesen. der wird dann beim zugriff auf datenfelder der subklasse immer addiert (was aber einiges an Mehraufwand ist, gehts anders?).

Problem sind aber auche die Methoden und Attribute IDs der Subklasse, denn die dürfen offensichtlich erst ab dem höchstens Wert+1 der Elternklasse anfangen. Wenn nun die Elternklasse aber um weitere Methoden erweitert wird, gibts Probleme, oder?

--
Sam mini os4.1 upd. 2 / e-uae 39bb2 / A4000D 3.0 & 3.9 2mbchip 8mbfast Ariadne_II ide DVD und HD / A500 3.1 (mkick) adide 50mb / Athlon ii X2 Ubuntu Linux

[ Dieser Beitrag wurde von AGSzabo am 22.12.2010 um 08:46 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

22.12.2010, 10:45 Uhr

Thore
Posts: 2266
Nutzer
> Mir ist noch nicht klar wo im ganzen Objekt die zeiger sind auf meine Unterklasse und Elternklasse.

Die musst du in deiner Objektstruktur definieren.
Alle Objekte sind erstmal von dem "gleichen" Typ z.B. GUIObject und wissen was für eine Klassifizierung sie haben, z.B. Button. (z.B. über ein Flag)
Das GUIObject hat dann z.B. Breite, Höhe, und so allgemeines, und einen Pointer zu seinem Parent. Über sein Parent wird das Offset errechnet wo das Objekt dann relativ liegt.

Beispiel (in C) und bitte nur als Beispiel sehen:

struct GUIObject
{
int L, T, W, H;
struct GUIObject Parent;
int ObjectType;
....
}
struct GUIButton
{
struct GUIObject Common;
APTR ClickHook;
char *Title;
....
}
struct GUIImage
{
struct GUIObject Common;
APTR ImageData;
...
}
Damit lassen alle Objekte ein Typecast auf GUIObject zu (In Assembler sowieso kein Thema) und über den Typ ist es eindeutig bestimmt.

Es gibt mehrere Möglichkeiten das zu machen, das ist eine davon.

[ - Antworten - Zitieren - Direktlink - ]

22.12.2010, 10:55 Uhr

AGSzabo
Posts: 1663
Nutzer
@Thore:

Ich glaube den Parent-Zeiger brauche ich nicht. Der GUIButton kann seinen Offset über seine Klasse erhalten, die wiederum ihn aus der Superklasse ausließt (die Klasse das GUIButton enthält einen Zeiger auf die Superklasse).

Problem sind immernoch die methoden-IDs, die für das GUIButton erst ab dem höchsten ID-Wert der Superklasse anfangen dürfen...
--
Sam mini os4.1 upd. 2 / e-uae 39bb2 / A4000D 3.0 & 3.9 2mbchip 8mbfast Ariadne_II ide DVD und HD / A500 3.1 (mkick) adide 50mb / Athlon ii X2 Ubuntu Linux

[ Dieser Beitrag wurde von AGSzabo am 22.12.2010 um 10:56 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

22.12.2010, 11:12 Uhr

Thore
Posts: 2266
Nutzer
> Ich glaube den Parent-Zeiger brauche ich nicht.

Denk an dein Garbage Collector, der deine Elemente wieder aufräumen will, an deine Clientausmaße deiner Objekte (falls sie verschiebbar sind), an Autoalignment etc pp, für all das ist es notwendig daß dein Objekt sein Vaterobjekt kennt. Und wenn du es momentan nicht brauchst, das sind nur 4 Bytes pro Objekt, das verschmerzt du :)

[ - Antworten - Zitieren - Direktlink - ]

22.12.2010, 11:14 Uhr

Thore
Posts: 2266
Nutzer
> Problem sind immernoch die methoden-IDs, die für das GUIButton erst ab dem höchsten ID-Wert der Superklasse anfangen dürfen...

Wie meinst du das? Bitte mit Beispiel. (Kanns mir schon denken aber ich will Gewissheit)

[ - Antworten - Zitieren - Direktlink - ]

22.12.2010, 13:18 Uhr

AGSzabo
Posts: 1663
Nutzer
@Thore:

Beispiel

Letzte Methode der Superklasse (die mit der höchsten ID) hat den Wert 5 (es gibt 5 Mehtoden)

Die Methoden der Subklasse fangen bei 6 an und hören bei 8 auf.

Wende ich eine Methode auf die Subklasse an, so versteht diese nur Methoden ab ID Wert 6 bis 8. Hat eine ID einen anderen Wert, wird an die Superklasse weitergeleitet.

Ich muss aber zum erstellen der Subklasse wissen, dass meine Methoden erst bei 6 anfangen drüfen.

Oder gibts da ne andere Lösung? Ich hoffe das Beispiel war ausreichend. :-)
--
Sam mini os4.1 upd. 2 / e-uae 39bb2 / A4000D 3.0 & 3.9 2mbchip 8mbfast Ariadne_II ide DVD und HD / A500 3.1 (mkick) adide 50mb / Athlon ii X2 Ubuntu Linux

[ - Antworten - Zitieren - Direktlink - ]

22.12.2010, 14:48 Uhr

Thore
Posts: 2266
Nutzer
Und wenn Du GUI 2.0 rausbringen willst, hast kein Platz mehr für neue Methoden oder ein heilloses Durcheinander.
Wenn Du feste IDs vergeben willst, reservier auf jeden fall nen Bereich und pass die Nummern so an, damit sie auch was miteinander zu tun haben.

Wenn Du ne 32 Bit Zahl als ID nimmst, hast mehr als genug IDs.
z.B. kannst du die ID so formatieren, daß Du eine Objektnummer mit einfließen lässt. z.B. erste 4 Hex Stellen Objektnummer, letzte 4 Hex Stellen Methoden ID
Beispiel Superklasse
$00000001 = Methode 1
$0000000A = Methode 10

Button beispiel
$00400001 = Methode 1 des Button, z.B. Klick Methode

Das ist nur ein Beispiel, wie genau man das unterteilt/definiert ist reine Geschmacksache. Du sollst eben schauen daß die Übersicht nicht abhanden kommt.
Hier hast Du neben den Methoden-IDs noch IDs für Attribute übrig, z.B. über eine Superfunktion prüfen ob dein Button gedrückt ist. z.B.

bool bIsPressed;
#define GUIOBJA_BUTTON_PRESSED 0x00400100
getObjectAttr(MyButton, GUIOBJA_BUTTON_PRESSED, bIsPressed);
if (bIsPressed) printf("Button ist gedrückt\n") else printf("Button ist nicht gedrückt\n");

Nur ein Beispiel.

[ - Antworten - Zitieren - Direktlink - ]

22.12.2010, 15:06 Uhr

AGSzabo
Posts: 1663
Nutzer
@Thore:

> Wenn Du feste IDs vergeben willst, reservier auf jeden fall nen Bereich und pass die Nummern so an, damit sie auch was miteinander zu tun haben.

Wie geht es ohne feste IDs?


> Objektnummer

Wo bekomme ich die her? Imo müsste es wenn dann eine klassennummer sein...
--
Sam mini os4.1 upd. 2 / e-uae 39bb2 / A4000D 3.0 & 3.9 2mbchip 8mbfast Ariadne_II ide DVD und HD / A500 3.1 (mkick) adide 50mb / Athlon ii X2 Ubuntu Linux

[ - Antworten - Zitieren - Direktlink - ]

22.12.2010, 15:12 Uhr

Der_Wanderer
Posts: 1229
Nutzer
Es gibt verschiedene Wege das alles zu realisieren.
Dein Problem ist halt, dass du Assembler verwendest, und daher erstmal den OOP Code von C++ oder einer anderen OOP Sprache nachprogrammieren musst.

Hier ist mein Vorschlag:

1. Methoden haben keine ID. Es sind Funktion Pointer, wie in einer Library. Eine Objekt Instanz kennst seine Funktionen immer.

2. Ein Objekt muss seine Eltern Klasse zur Laufzeit nicht kennen. Es kennt nichts, ausser sich selbst, und das steht zur Kompilezeit immer fest.
Generell stellst du dir das evlt. falsch vor. Eine Klasse gibt es nicht in Form von Daten. Es ist nur eine Definition in Gedanken, die nach dem Kompilieren nicht mehr existiert, genauso wie Structs oder Variablen. Alles sind am Ende nur noch Speicherzugriffe an bestimmte Offsets.
(Debug-Informationen oder Call-by-Name sind hier was anderes, aber das brauchst du erstmal nicht)

3. Vergiss das Wort "Garbadge Collector". Du gibst deine Objekte in gewohnter Manier frei - mit Hilfe der Destructor Funktion (ich sage jetzt absichtlich nicht Methode), die es für jede Klasse gibt.

4. Bei dem Beispiel mit dem ImageButton hat Holger natürlich recht, aber ich wollte es jetzt verkomplizieren, das war ja nur ein Beispiel für Vererbung. Im GUI System solltest du die mölgichst mächtige "Drawables" implementieren, sodass du ohne zutun z.b. Bilder überall einfügen kannst, und das nicht jedesmal individuell implemenieren musst.
z.b. eine "RichText()" Funktion, die auch Bilder im Fliesstext kannt.
AmigaOS kennt z.b.
code:
Text(rp,"Hallo",5)

, und damit Zeichnest du deine Texte für Label Button Karteitreiter etc.
Hast du aber einmal "RichText()" geschrieben, werden auf einmal alle Widgets viel "mächger".
So ein RichText könnte z.b. sowas machen:
code:
RichText("\i'AISS:Exclamation'\bHallo"

was dann Hallo in Bold macht und vorher das AISS Bildchen zeichnet.
Dafür solltest du dann am besten noch so wie in NTUI auto-resizing der Bilder machen, sodass sie Fontgröße bekommen.

Bild: http://www.hd-rec.de/pics/ntui_status20.png


--
--
Author of
HD-Rec, Sweeper, Samplemanager, ArTKanoid, Monkeyscript, Toadies, AsteroidsTR, TuiTED, PosTED, TKPlayer, AudioConverter, ScreenCam, PerlinFX, MapEdit, AB3 Includes und viele mehr...
Homepage: http://www.hd-rec.de


[ Dieser Beitrag wurde von Der_Wanderer am 22.12.2010 um 15:19 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

22.12.2010, 16:40 Uhr

Thore
Posts: 2266
Nutzer
> Wie geht es ohne feste IDs?

Ohne dann. So oder ähnlich wie Thomas es beschreibt. Ich schrieb ja auch "wenn du feste IDs verwenden willst, machs so oder ähnlich" (Betonung auf "wenn", nicht auf "feste").
Ist reine Geschmacksache beide Wege können zum Ziel führen. Je nachdem wie Du es implementierst.
Im MUI Style wäre es mit diesen IDs und im Intuition Style eben ohne (hier haben wir zwei Beispiele)

[ - Antworten - Zitieren - Direktlink - ]

22.12.2010, 19:09 Uhr

Der_Wanderer
Posts: 1229
Nutzer
Nochmal ein simples Beispiel:

code:
+-----------------------------------------------------------------------------
  | 
  | Struct sprite {                     ; define a general "sprite" structure
  |   x.f:y.f
  |   image.l
  | }
  | 
  | Function sprite::Draw(*this.sprite) {   ; define a general draw method
  |   DrawImage(this\image,this\x,this\y)   ; use fictive function DrawImage()
  | }
  | 
  | Struct sprite->alien {  ; define alien sub class, by inheriting from sprite
  |   energy.f
  | }
  | 
  | Function alien::Hit(*this.alien,energy.f) { ; define a method for the alien
  |   this\energy - energy
  | }
  | 
  | *myAlien.alien = New()              ; create a new alien object
  | 
  | ; call the methods on myAlien object:
  | myAlien\Hit(10)                     ; this works only on aliens!
  | myAlien\Draw()                      ; this is a method inherited from sprite
  | Delete(*myAlien)                    ; delete the alien object
  |
  +-----------------------------------------------------------------------------

Also was haben wir hier?

Es gibt eine Klasse "Sprite", die hat Informationen über die Position x/y und das Bild des Sprites.
Ausserdem gibt es eine Methode "Draw" zum anzeigen des Sprites.

Der eigentlich Struct in Assembler würde so aussehen:
code:
float x
float y
ptr image
ptr Draw()
ptr Delete() ; <= Destructor, haben alle Objekte


Jetzt können wir Subklassen vom Sprite bilden, z.b. für ein Alien Gegner.
Dort kommt noch die Energie dazu und die Methode Hit().

Der Resultierend Struct aus ASM sicht wäre:

code:
ptr Delete() ; <= Destructor, haben alle Objekte
float x
float y
ptr image
ptr Draw()
;------------------- hier kommen Felder von "Alien"
float energy
ptr Hit()


Zur Laufzeit muss das Alien nicht mehr wissen, dass es ein Sprite ist. Auch ein Array von Sprites muss nicht wissen, dass dort Teilweise Aliens drin sind, da ja alles was ein Sprite ausmacht vorhanden ist.
Man ruft einfach Draw auf. Draw könnte z.b. jetzt auch Überladen werden, aber in dem Fall mache ich das aber nicht.
Hit() darf natürlich nur auf einem Alien aufgerufen werden, aber wenn du ein Sprite Struct hast dann kennst du ja Hit auch gar nicht, also kommt du erst gar nicht in die Versuchung.
Das ist schon mehr oder weniger alles über die OOP Magie.

Der Anwender kann nun auch selbst Sprite, oder auch Alien, Subklassen und weitere Felder hinzufügen, weiter Methoden oder auch vorhandene Methoden überladen. Alles, was ein OOP braucht also.

Note:

New = AllocMem + Init
Delete = Deinit + FreeMem

--
--
Author of
HD-Rec, Sweeper, Samplemanager, ArTKanoid, Monkeyscript, Toadies, AsteroidsTR, TuiTED, PosTED, TKPlayer, AudioConverter, ScreenCam, PerlinFX, MapEdit, AB3 Includes und viele mehr...
Homepage: http://www.hd-rec.de



[ Dieser Beitrag wurde von Der_Wanderer am 22.12.2010 um 19:10 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

22.12.2010, 19:47 Uhr

AGSzabo
Posts: 1663
Nutzer
@Der_Wanderer:

Ok. Woher weis ich nun, wenn ich die Methode "draw" aufrufen will, wo ich den pointer darauf finde?
--
Sam mini os4.1 upd. 2 / e-uae 39bb2 / A4000D 3.0 & 3.9 2mbchip 8mbfast Ariadne_II ide DVD und HD / A500 3.1 (mkick) adide 50mb / Athlon ii X2 Ubuntu Linux

[ - Antworten - Zitieren - Direktlink - ]

22.12.2010, 21:07 Uhr

Der_Wanderer
Posts: 1229
Nutzer
Der Offset ist zur Compilezeit immer bekannt.
In unserem Beispiel wäre das Objekt Pointer + 12, für alle Objekte von "Sprite" und dessen Unterklassen.

IDs oder ähnliches haben nichts mit OOP zu tun, im Gegenteil. Das wird eher benutzt um OOP nachzuahmen, ist aber keine wirklich gute Idee und sehr limitiert.

Ich benutze sowas nur bei Getters und Setters, da es nicht praktikabel ist für jedes Detail eine Methode zu machen.

Achja, nochwas: niemals den Zugriff auf Structs erlauben ausserhalb des Codes der den Struct definiert.
Sonst ist man Ruckzuck in der Falle, wenn es um Erweiterungen geht (siehe AmigaOS).

Beispiel:
Du speicherst das Layout deiner Widgers mit x/y und width/height.
Nun stellst du fest, dass x1/y1 und x2/y2 intern sehr viel komfortabler ist und möchtest das umstellen. Wenn du nun den Zugriff auf width und height per Struct zugelassen hast, sitzt du in der Falle.
Über Getters und Setters kein Problem, da man es ohne größere Umstände umrechnen kann.

--
--
Author of
HD-Rec, Sweeper, Samplemanager, ArTKanoid, Monkeyscript, Toadies, AsteroidsTR, TuiTED, PosTED, TKPlayer, AudioConverter, ScreenCam, PerlinFX, MapEdit, AB3 Includes und viele mehr...
Homepage: http://www.hd-rec.de


[ - Antworten - Zitieren - Direktlink - ]

22.12.2010, 21:27 Uhr

AGSzabo
Posts: 1663
Nutzer
@Der_Wanderer:

Ich verstehe das nicht wie der offset zur compilezeit bekannt sein soll und wo ich den her bekommen. ich soll doch keine festen offsets in den strukturen benutzen? mache ich eh nicht... leider habe ich aber methoden IDs.

Außerdem brauche ich für die getters und setters in subklassen auch IDs die noch frei sind.
--
Sam mini os4.1 upd. 2 / e-uae 39bb2 / A4000D 3.0 & 3.9 2mbchip 8mbfast Ariadne_II ide DVD und HD / A500 3.1 (mkick) adide 50mb / Athlon ii X2 Ubuntu Linux

[ - Antworten - Zitieren - Direktlink - ]

22.12.2010, 22:23 Uhr

Der_Wanderer
Posts: 1229
Nutzer
Natürlich ist der Struct zur Compilezeit bekannt. Sonst wäre es ja kein Struct. Du greifst doch auch auf die Koordinaten irgendwie zu. Genauso kannst du doch auf die Funktion Pointer zugreifen, was hindert dich daran?
Normalerweise mit einem offset, z.b.

JSR DRAW_METHOD_PTR(a0)

Wobei a0 den Objekt Pointer enthält, und DRAW_METHD_PTR der offset ist.

Die Getters and Setters IDs sind ja keine Methoden IDs.
Wenn der Benutzer eigene Attribute vergeben will, dann machst du das so wie bei Taglists üblich (das ist ja genau das gleiche).
Du definierst

XUIA_USER = ...
und der Benutzer fügt seine IDs ab diesem Wert aufwärts hinzu. Den setzt du hinreichend gross.

--
--
Author of
HD-Rec, Sweeper, Samplemanager, ArTKanoid, Monkeyscript, Toadies, AsteroidsTR, TuiTED, PosTED, TKPlayer, AudioConverter, ScreenCam, PerlinFX, MapEdit, AB3 Includes und viele mehr...
Homepage: http://www.hd-rec.de


[ - Antworten - Zitieren - Direktlink - ]

22.12.2010, 22:52 Uhr

AGSzabo
Posts: 1663
Nutzer
@Der_Wanderer:

wie wäre es wenn ich als autor einer klasse vorgebe ab welcher ID meine benutzer-subklassen ihre IDs definieren dürfen? damit kann ich mir so viel platz für meine IDs reservieren wie ich glaube dass ich (höchstens irgendwann mal oder auch nicht) brauche.
--
Sam mini os4.1 upd. 2 / e-uae 39bb2 / A4000D 3.0 & 3.9 2mbchip 8mbfast Ariadne_II ide DVD und HD / A500 3.1 (mkick) adide 50mb / Athlon ii X2 Ubuntu Linux

[ Dieser Beitrag wurde von AGSzabo am 22.12.2010 um 22:53 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

22.12.2010, 23:00 Uhr

Der_Wanderer
Posts: 1229
Nutzer
Genau. Das macht die XUIA_USER ID.

Ich habe das mal so genannt, "XUI" heisst dein Toolkit, "A" steht für Attribute, und "_USER" ist der Name der ersten freien ID.

Im NTUI ListView widget wäre das also

NTUILVA_USER

NTUI - LivtView - Attribute - User


--
--
Author of
HD-Rec, Sweeper, Samplemanager, ArTKanoid, Monkeyscript, Toadies, AsteroidsTR, TuiTED, PosTED, TKPlayer, AudioConverter, ScreenCam, PerlinFX, MapEdit, AB3 Includes und viele mehr...
Homepage: http://www.hd-rec.de


[ - Antworten - Zitieren - Direktlink - ]


-1- 2 [ - Beitrag schreiben - ]


amiga-news.de Forum > Programmierung > wie programmiert man "vererbung" [ - Suche - Neue Beiträge - Registrieren - Login - ]


.
Impressum | Datenschutzerklärung | Netiquette | Werbung | Kontakt
Copyright © 1998-2024 by amiga-news.de - alle Rechte vorbehalten.
.