Wie andere bemerkt haben, ist es sicherlich möglich, dies in C zu implementieren. Es ist nicht nur möglich, es ist ein ziemlich verbreiteter Mechanismus. Das am häufigsten verwendete Beispiel ist wahrscheinlich die Dateideskriptorschnittstelle in UNIX. Ein read()
Aufruf eines Dateideskriptors wird an eine Lesefunktion gesendet, die für das Gerät oder den Dienst spezifisch ist, der diesen Dateideskriptor bereitgestellt hat (war es eine Datei? War es ein Socket? War es eine andere Art von Gerät?).
Der einzige Trick besteht darin, den Zeiger auf den konkreten Typ vom abstrakten Typ wiederherzustellen. Bei Dateideskriptoren verwendet UNIX eine Nachschlagetabelle, die für diesen Deskriptor spezifische Informationen enthält. Wenn Sie einen Zeiger auf ein Objekt verwenden, ist der vom Benutzer der Schnittstelle gehaltene Zeiger der Typ "base", nicht der Typ "derived". C hat keine Vererbung an sich, aber es garantiert, dass der Zeiger auf das erste Element eines struct
gleich dem Zeiger des enthaltenen struct
ist. Sie können also den "abgeleiteten" Typ wiederherstellen, indem Sie die Instanz der "Basis" zum ersten Element des "abgeleiteten" Typs machen.
Hier ist ein einfaches Beispiel mit einem Stapel:
struct Stack {
const struct StackInterface * const vtable;
};
struct StackInterface {
int (*top)(struct Stack *);
void (*pop)(struct Stack *);
void (*push)(struct Stack *, int);
int (*empty)(struct Stack *);
int (*full)(struct Stack *);
void (*destroy)(struct Stack *);
};
inline int stack_top (struct Stack *s) { return s->vtable->top(s); }
inline void stack_pop (struct Stack *s) { s->vtable->pop(s); }
inline void stack_push (struct Stack *s, int x) { s->vtable->push(s, x); }
inline int stack_empty (struct Stack *s) { return s->vtable->empty(s); }
inline int stack_full (struct Stack *s) { return s->vtable->full(s); }
inline void stack_destroy (struct Stack *s) { s->vtable->destroy(s); }
Nun, wenn ich einen Stapel mit einem festen Größe Array implementieren will ich etwas tun könnte:
struct StackArray {
struct Stack base;
int idx;
int array[STACK_ARRAY_MAX];
};
static int stack_array_top (struct Stack *s) { /* ... */ }
static void stack_array_pop (struct Stack *s) { /* ... */ }
static void stack_array_push (struct Stack *s, int x) { /* ... */ }
static int stack_array_empty (struct Stack *s) { /* ... */ }
static int stack_array_full (struct Stack *s) { /* ... */ }
static void stack_array_destroy (struct Stack *s) { /* ... */ }
struct Stack * stack_array_create() {
static const struct StackInterface vtable = {
stack_array_top, stack_array_pop, stack_array_push,
stack_array_empty, stack_array_full, stack_array_destroy
};
static struct Stack base = { &vtable };
struct StackArray *sa = malloc(sizeof(*sa));
memcpy(&sa->base, &base, sizeof(base));
sa->idx = 0;
return &sa->base;
}
Und wenn Ich wollte stattdessen einen Stapel mit einer Liste implementieren:
struct StackList {
struct Stack base;
struct StackNode *head;
};
struct StackNode {
struct StackNode *next;
int data;
};
static int stack_list_top (struct Stack *s) { /* ... */ }
static void stack_list_pop (struct Stack *s) { /* ... */ }
static void stack_list_push (struct Stack *s, int x) { /* ... */ }
static int stack_list_empty (struct Stack *s) { /* ... */ }
static int stack_list_full (struct Stack *s) { /* ... */ }
static void stack_list_destroy (struct Stack *s) { /* ... */ }
struct Stack * stack_list_create() {
static const struct StackInterface vtable = {
stack_list_top, stack_list_pop, stack_list_push,
stack_list_empty, stack_list_full, stack_list_destroy
};
static struct Stack base = { &vtable };
struct StackList *sl = malloc(sizeof(*sl));
memcpy(&sl->base, &base, sizeof(base));
sl->head = 0;
return &sl->base;
}
Die Implementierungen des Stapels ope Rationen würden einfach das struct Stack *
zu dem, was es weiß, es sollte sein. Zum Beispiel:
static int stack_array_empty (struct Stack *s) {
struct StackArray *sa = (void *)s;
return sa->idx == 0;
}
static int stack_list_empty (struct Stack *s) {
struct StackList *sl = (void *)s;
return sl->head == 0;
}
Wenn ein Benutzer von einem Stapel einen Stapelbetrieb auf der Stack-Instanz aufruft, versenden die Operation auf den entsprechenden Vorgang in den vtable
. Diese vtable
wird von der Erstellungsfunktion mit den Funktionen initialisiert, die ihrer jeweiligen Implementierung entsprechen. Also:
Stack *s1 = stack_array_create();
Stack *s2 = stack_list_create();
stack_push(s1, 1);
stack_push(s2, 1);
stack_push()
wird sowohl genannt auf s1
und s2
. Aber für s1
wird es an stack_array_push()
versandt, während für s2
, wird es an stack_list_push()
senden.
haben kann Sie dies tun, indem Sie die Art, wie es in anderen Sprachen "unter der Haube" getan wird. In C++ zum Beispiel wird der Versand durchgeführt, indem ein Index in eine Tabelle von Funktionszeigern nachgeschlagen wird. Wenn Sie etwas unterklassifizieren, fügen Sie Ihre Träger am Ende der Tabelle hinzu. Sie können eine Struktur von Funktionsträgern verwenden, um die Tabelle zu repräsentieren und einfach die strikte Klasse der Elternklasse am Anfang von Ihnen für die Vererbung einzuschließen. – Will
Lassen Sie uns dies zu einer praktischen Diskussion machen, indem Sie sagen, was Sie in Ihrer Anwendung erreichen möchten. Dann können wir Codebeispiele und praktikable Ideen liefern. Ihre aktuelle Frage ist eher vage und kann in 15 Minuten mit Google beantwortet werden. –
gut jemand würde es vorziehen, wenn sie etwas Code bereits in c geschrieben haben, aber etwas Funktionalität hinzufügen. anstatt von Grund auf mit OOlanguage zu schreiben. – Dineshkumar