Verkettete Liste: verschiedene Objekte ablegen und deren Funktionen aufrufen
-
Du legst hier global Speicher an und initialisierst ihn gleich noch
na klar so wie man das eben macht.
Es gibt nur eine Instanz ObjektA
naja das ist nur zum teil richtig, denn es gibt nur eine "classe A" von dieser klasse werden dann die objecte abgeleitet.
Du frägst die Adresse dieser Instanz ab
ich frage nicht die adresse der instanz sondern die adresse der classe ab das ist ein unterschied.
ich denke wir haben einfach unterschiedliche ansichten wie oop aussehen muß.
lg lolo
-
stefan-tiger schrieb:
Warum möchte ich das nicht haben:
zu 1.: Wenn jemand garkein ObjektA angelgen möchte bekommt er doch eins untergeschoben
zu 2.: Es soll möglich sein viele Instanzen von ObjektA zu erzeugen
zu 3.: Ich müsste für jede Instanz von ObjektA die Adresse abfragen; das geht nicht weil ich nicht weiss wieviele ObjektA-Instanzen zur Laufzeit angelegt werden; zudem wäre es umständlichEin dynamisch erzeugtes ObjektX muss mit Werten initialisiert werden, sonst ist die Abfrage eines Objekt-Typs bzw. ein Zugriff auf einen Funktionszeiger nicht möglich. Bei der Initialisierung kannst du diesem Objekt einen Funktionszeiger verpassen.
Haben alle Objekte ( Objekt1, ... , ObjektN ) als erstes Element einen Fuktionszeiger z.B.
void (*func_ptr)(void* object), kannst du dir eine Art Schablone erstellen:typedef struct objectX ObjectX; struct objectX { void (*func_ptr)(void* object); };
Der Zeiger auf diese Schablone ist dein 'Universalschlüssel' für den Zugang zu den Funktionen der Objekte Objekt1, ... , ObjektN. Damit hast du einen Direktzugriff auf die Funktionen der zugehörigen Objekte, ohne einen Objekttyp abragen zu müssen. Guckst du:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> typedef struct objectX ObjectX; typedef struct objectA ObjectA; typedef struct objectB ObjectB; typedef struct list List; struct objectX { void (*func_ptr)(void* object); }; struct objectA { void (*func_ptr)(void* object); int value; }; struct objectB { void (*func_ptr)(void* object); char buf[BUFSIZ]; }; struct list { void* object; List* next; }; void func_objectA ( void* object ) { ObjectA* p = object; printf ( "%d\n", p->value ); } void func_objectB ( void* object ) { ObjectB* p = object; printf ( "%s\n", p->buf ); } void init_objectA ( ObjectA* p ) { p->func_ptr = func_objectA, p->value = 707; } void init_objectB ( ObjectB* p ) { p->func_ptr = func_objectB, strncpy ( p->buf, "Hello World", sizeof(p->buf)-1 ); } void process_list ( List* l ) { ObjectX* px; while ( l != NULL ) px = l->object, px->func_ptr ( l->object ), l = l->next; } int main() { List* L = calloc ( 2, sizeof ( *L ) ); ObjectA* A = malloc ( sizeof( *A ) ); ObjectB* B = malloc ( sizeof( *B ) ); if ( errno ) { free ( A ), free ( B ), free ( L ); return 1; }; init_objectA ( A ), init_objectB ( B ); L[0].object = B, L[0].next = &L[1], L[1].object = A, L[1].next = NULL; process_list ( L ); free ( A ), free ( B ), free ( L ); return 0; }
-
evtl. schaust dir mal in java script das prototype object und konzept an. wenn man verstanden hat was da passiert lässt sich oop auch easy in c umsetzen und man kommt nicht mit komischen vorstellungen daher die klassen structur und methoden in jede instanz mit reinzukopieren.
lg lolo
-
noobLolo schrieb:
...
naja das ist nur zum teil richtig, denn es gibt nur eine "classe A" von dieser klasse werden dann die objecte abgeleitet.
...
ich frage nicht die adresse der instanz sondern die adresse der classe ab das ist ein unterschied.ich denke wir haben einfach unterschiedliche ansichten wie oop aussehen muß.
...
Korrigier mich wenn ich was falsches schreibe:
In deinem Code ist Klasse=Instanz und es gibt sie nur einmal. Und dieses eine mal ist auch noch zur Compile-Zeit und nicht zur Laufzeit angelegt.
Daher ist es in deinem Beispiel auch kein Problem die Adresse der "Klasse" abzufragen.Ich möchte aber mehrere Instanzen zur Laufzeit anlegen.
Ich weiss nicht ob ich ein anderes Verständnis von OOP habe, aber in C ist für mich das was in C++ eine "class" ist ein "typedef struct".
Wichtig ist mir hierbei das "typedef", denn eine Klasse in C++ belegt an sich noch kein Speicher und kann nicht als "lebendes Objekt" vendendet werden, das wird erst mit einer Instanz möglich (oder nicht?).
-
Hallo Big Brother,
deinen ersten Satz verstehe ich nicht. Ist das ein Problem für Dich?
Für mich ist das kein Problem, da ich eine new-Funktion für meine Objekte schreibe in der das "malloc" gemacht wird und initialisiert wird.Ansonsten kann ich dein Beispiel nachvollziehen, danke dafür.
Hierbei sind aber die Einschränkungen gegeben, dass
(a) alle Klassen diesen Funktionspointer haben müssen
(b) der Funktiinspointer im struct aller Klassen immer an der selben Stelle steht
(c) der Funktionspointer immer die selbe Signatur in allen Klassen haben mussDer Vorteil dieser Lösung ist allerdings, dass es dem Basis-Klassen-Konzept von C++ näher kommt und dass die Implementierung zwischen der Liste und den Objekten besser getrennt ist.
-
Hallo Big Brother,
ich hab grad noch was gesehn was mit nicht gefällt bei der Lösung:
Die "Member"-Funktionen haben void-Pointer als Parameter.
-
stefan-tiger schrieb:
Hallo Big Brother,
deinen ersten Satz verstehe ich nicht. Ist das ein Problem für Dich?
Nein, kein Problem. Du hast ne new-Funktion, hast sich also erledigt.
stefan-tiger schrieb:
Hierbei sind aber die Einschränkungen gegeben, dass
(a) alle Klassen diesen Funktionspointer haben müssen
Oder eine Art Objekt ID, die ebenfalls alle Strukturen haben müssen, wobei der Zugriff auf die Funktionen umständlicher ist und länger dauert.
stefan-tiger schrieb:
(b) der Funktiinspointer im struct aller Klassen immer an der selben Stelle steht
Ja, die Reihenfolge der Strukturelemente ist eingeschränkt. Inwiefern ist das für Dich problematisch?
stefan-tiger schrieb:
(c) der Funktionspointer immer die selbe Signatur in allen Klassen haben muss
Dank der Zeigerübergabe aber flexibel ist.
stefan-tiger schrieb:
Der Vorteil dieser Lösung ist allerdings, dass es dem Basis-Klassen-Konzept von C++ näher kommt und dass die Implementierung zwischen der Liste und den Objekten besser getrennt ist.
Je näher an C++ ,desto besser?
Da drängt sich mir die Frage auf: Warum schreibst Du das nicht gleich in C++ ?stefan-tiger schrieb:
Die "Member"-Funktionen haben void-Pointer als Parameter.
Was gefällt Dir daran nicht? Du kannst auch nen unsigned long nehmen oder sowas.
Gruß,
B.B.
-
Big Brother schrieb:
...
stefan-tiger schrieb:
(b) der Funktiinspointer im struct aller Klassen immer an der selben Stelle steht
Ja, die Reihenfolge der Strukturelemente ist eingeschränkt. Inwiefern ist das für Dich problematisch?
...stefan-tiger schrieb:
Der Vorteil dieser Lösung ist allerdings, dass es dem Basis-Klassen-Konzept von C++ näher kommt und dass die Implementierung zwischen der Liste und den Objekten besser getrennt ist.
Je näher an C++ ,desto besser?
Da drängt sich mir die Frage auf: Warum schreibst Du das nicht gleich in C++ ?stefan-tiger schrieb:
Die "Member"-Funktionen haben void-Pointer als Parameter.
Was gefällt Dir daran nicht? Du kannst auch nen unsigned long nehmen oder sowas.
Gruß,
B.B.Problematisch ist es nicht direkt. Ich versuche nur eine so weit wie möglich allgemeingültige Lösung zu finden. Die Strukturen haben dann eben gewissen "organisatorischen" Regelen entsprechen und sind nichtmehr völlig unabhängig (wären sie mit der Typ-Kennung auch nicht 100%).
Warum ich nicht C++ verwende? Nun, ich denke ich kann mehr über die "internen" Dinge eines Programms lernen wenn ich in C Dinge selbst machen muss die in C++ inklusive sind bzw. durch Templates und umfangreiche Libraies gekapselt sind.
Natürlich kann man sich auch deren Implementierungen ansehen...An den void-Parametern gefällt mir nicht, dass hier der Code an einer Stelle verändert wurde, weil an einer anderen Stelle eine bestimmte Implementierung gewählt wurde. Anders ausgedrückt: Ohne die verkettete Liste würde man den Member-Funktionen der Klassen einen Pointer des "richtigen" Klassen-Typs übergeben, statt einen void-Pointer.
Das ist eine Design-Regel die man z.B. anderen Programmierern die Klassen zuliefern möchten mitgeben muss. Das macht es jedenfall nicht einfacher.
-
stefan-tiger schrieb:
An den void-Parametern gefällt mir nicht, dass hier der Code an einer Stelle verändert wurde, weil an einer anderen Stelle eine bestimmte Implementierung gewählt wurde. Anders ausgedrückt: Ohne die verkettete Liste würde man den Member-Funktionen der Klassen einen Pointer des "richtigen" Klassen-Typs übergeben, statt einen void-Pointer.
Das ist eine Design-Regel die man z.B. anderen Programmierern die Klassen zuliefern möchten mitgeben muss. Das macht es jedenfall nicht einfacher.Ok, ist hier auch machbar und es gefällt mir auch besser als die void*
Alternative:void func_objectA ( ObjectA* object ) { printf ( "%d\n", object->value ); } void func_objectB ( ObjectB* object ) { printf ( "%s\n", object->buf ); }
-
stefan-tiger schrieb:
In deinem Code ist Klasse=Instanz und es gibt sie nur einmal. Und dieses eine mal ist auch noch zur Compile-Zeit und nicht zur Laufzeit angelegt.
Daher ist es in deinem Beispiel auch kein Problem die Adresse der "Klasse" abzufragen.ich betrachtete objectA und objectB als klasse und Entry_t wäre dann eine instanz der klasse.
denn eine Klasse in C++
der ganze vergleich mit c++ hinkt ein bischen, da es sich eben nicht vergleichen lässt. oop in c und oop in c++ sind eben unterschiedliche sachen. ich weiß jetzt nicht genau was c++ aus den klassen macht. aber du solltest bedenken das c++ auch nicht zaubern kann und evtl. die structuren ohne dein wissen anlegt denn irgendwoher muß auch c++ wissen welche function es aufrufen soll. allerdings kann auch der compiler gewisse optimierungen treffen und somit auf den umweg über eine klasse verzichten und die function direkt aufrufen.
lg lolo
-
Big Brother schrieb:
void func_objectA ( ObjectA* object ) { printf ( "%d\n", object->value ); } void func_objectB ( ObjectB* object ) { printf ( "%s\n", object->buf ); }
Hey, das gefällt mir! Sieht meiner Eventqueue sehr ähnlich, deren Draft basiert allerdings auf void*, damals hatte ich aber noch dooferweise eine Trennung von Datenlisten und Funktionslisten und mußte mir irgendeinen Blödsinn ausdenken, Datentypen zu parsen, bis mir irgendwann aufgefallen ist, daß es das gar nicht braucht.
stefan-tiger schrieb:
Korrigier mich wenn ich was falsches schreibe:
In deinem Code ist Klasse=Instanz und es gibt sie nur einmal. Und dieses eine mal ist auch noch zur Compile-Zeit und nicht zur Laufzeit angelegt.
Daher ist es in deinem Beispiel auch kein Problem die Adresse der "Klasse" abzufragen.Deswegen vermeide ich bei C die Begriffe wie Klasse, Objekt und Instanz. Um Methoden und Daten zusammenzuschnallen brauchst Du entweder structs oder Klassen, trotzdem sind sie nicht dasselbe. Zur Compiletime ist eine struct auch nur ein abstraktes Gebilde, eine Definition, ob da ein Funktionspointer drin ist oder nicht. Und es ist völlig egal, ob Du die struct als Frame-element oder als Bestandteil einer Liste auf dem Heap anlegst, das Speicheranlegen erfolgt zur Runtime. Dann ist es aber immer noch nur ein Stück Speicher, das Du sinnvoll füllen solltest und der Funktionspointer typisiert das Ding eben auch erst zur Laufzeit. Erst damit ist das Ding "instantiiert".
Ergo Klasse != Instanzstefan-tiger schrieb:
Ich möchte aber mehrere Instanzen zur Laufzeit anlegen.
Kannst Du ja, bis der Speicher voll ist. Jedes Listenelement ist eine Instanz.
stefan-tiger schrieb:
Wichtig ist mir hierbei das "typedef", denn eine Klasse in C++ belegt an sich noch kein Speicher und kann nicht als "lebendes Objekt" vendendet werden, das wird erst mit einer Instanz möglich (oder nicht?).
Na und, reitest Du deshalb auf dem typedef rum? Du kannst tausend structs deklarieren, sie nehmen zur Runtime kein einziges Byte weg. Der typedef ist nur eine Annehmlichkeit für Schreibfaule.
Daß der "Schlüssel" (nach BigBrother-Definition) passen muß, kann man positiv oder negativ sehen, ich bin bisher bei void * geblieben, ohne mich weiter zu verkopfen, weil ich in "Zeiger auf irgendwas" die Unverbindlichkeit habe, die mir so vorschwebte. Aber schön, sowas wieder aufgerollt zu sehen, ich werd' mich mal in einer ruhigen Minute nach Jahren unreflektierten Einsatzes über meinen Kram wieder hermachen und gucken, ob das was bringt.
Danke, B.B. für die Denkanstöße!