Gibt es eine Möglichkeit, OO-ähnlichen Code in der C
Programmiersprache zu schreiben ?
Siehe auch:
Gefunden durch Suchen auf "[c] oo".
Gibt es eine Möglichkeit, OO-ähnlichen Code in der C
Programmiersprache zu schreiben ?
Siehe auch:
Gefunden durch Suchen auf "[c] oo".
Antworten:
Der erste C ++ - Compiler ("C mit Klassen") würde tatsächlich C-Code generieren, das ist also definitiv machbar.
Grundsätzlich ist Ihre Basisklasse eine Struktur; Abgeleitete Strukturen müssen die Basisstruktur an der ersten Position enthalten, damit ein Zeiger auf die "abgeleitete" Struktur auch ein gültiger Zeiger auf die Basisstruktur ist.
typedef struct {
data member_x;
} base;
typedef struct {
struct base;
data member_y;
} derived;
void function_on_base(struct base * a); // here I can pass both pointers to derived and to base
void function_on_derived(struct derived * b); // here I must pass a pointer to the derived class
Die Funktionen können als Funktionszeiger Teil der Struktur sein, so dass eine Syntax wie p-> call (p) möglich wird, Sie aber dennoch explizit einen Zeiger auf die Struktur an die Funktion selbst übergeben müssen.
struct derived*
an function_on_base
wird nicht kompiliert. struct derived*
ist ein anderer Typ als struct base*
selbst wenn die Adresse korrekt ist; Wenn Sie jedoch den Zeiger von derived*
auf base*
umwandeln, funktioniert dies (Sie verpassen jedoch die Typprüfung zur Kompilierungszeit und erhalten stattdessen zur Laufzeit einen Absturz). @PatrickCollins Zwingende ist möglich in C: pastebin.com/W5xEytbv
Ein üblicher Ansatz besteht darin, eine Struktur mit Zeigern auf Funktionen zu definieren. Dies definiert 'Methoden', die für jeden Typ aufgerufen werden können. Subtypen setzen dann ihre eigenen Funktionen in dieser gemeinsamen Struktur und geben sie zurück.
Zum Beispiel gibt es im Linux-Kernel struct:
struct inode_operations {
int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
struct dentry * (*lookup) (struct inode *,struct dentry *,
struct nameidata *);
...
};
Jeder registrierte Art von Dateisystem registriert dann seine eigenen Funktionen create
, lookup
und die verbleibenden Funktionen. Der Rest des Codes kann dann generische inode_operations verwenden:
struct inode_operations *i_op;
i_op -> create(...);
C ++ ist nicht weit von C.
Klassen sind Strukturen mit einem versteckten Zeiger auf eine Tabelle von Funktionszeigern namens VTable. Die Vtable selbst ist statisch. Wenn Typen auf Vtables mit derselben Struktur verweisen, Zeiger jedoch auf eine andere Implementierung verweisen, erhalten Sie Polymorphismus.
Es wird empfohlen, die Aufruflogik in Funktionen zu kapseln, die die Struktur als Parameter verwenden, um Code-Unordnung zu vermeiden.
Sie sollten auch die Instanziierung und Initialisierung von Strukturen in Funktionen (dies entspricht einem C ++ - Konstruktor) und Löschen (Destruktor in C ++) einkapseln. Dies sind sowieso gute Praktiken.
typedef struct
{
int (*SomeFunction)(TheClass* this, int i);
void (*OtherFunction)(TheClass* this, char* c);
} VTable;
typedef struct
{
VTable* pVTable;
int member;
} TheClass;
So rufen Sie die Methode auf:
int CallSomeFunction(TheClass* this, int i)
{
(this->pVTable->SomeFunction)(this, i);
}
Ich habe mir die Antworten aller anderen angesehen und mir Folgendes ausgedacht:
#include <stdio.h>
typedef struct
{
int (*get)(void* this);
void (*set)(void* this, int i);
int member;
} TheClass;
int Get(void* this)
{
TheClass* This = (TheClass*)this;
return This->member;
}
void Set(void* this, int i)
{
TheClass* This = (TheClass*)this;
This->member = i;
}
void init(TheClass* this)
{
this->get = &Get;
this->set = &Set;
}
int main(int argc, char **argv)
{
TheClass name;
init(&name);
(name.set)(&name, 10);
printf("%d\n", (name.get)(&name));
return 0;
}
Ich hoffe das beantwortet einige Fragen.
mylib_someClass_aMethod(this)
ist eine gute Möglichkeit.
Anhang B des Artikels Open Reusable Object Models von Ian Piumarta und Alessandro Warth von VPRI ist eine Implementierung eines Objektmodells in GNU C mit ca. 140 Codezeilen. Es ist eine faszinierende Lektüre!
Hier ist die nicht zwischengespeicherte Version des Makros, das Nachrichten an Objekte mit einer GNU-Erweiterung an C ( Anweisungsausdruck ) sendet :
struct object;
typedef struct object *oop;
typedef oop *(*method_t)(oop receiver, ...);
//...
#define send(RCV, MSG, ARGS...) ({ \
oop r = (oop)(RCV); \
method_t method = _bind(r, (MSG)); \
method(r, ##ARGS); \
})
Im gleichen doc, haben einen Blick auf die object
, vtable
, vtable_delegated
und symbol
structs und die _bind
und vtable_lookup
Funktionen.
Prost!
Die Dateifunktionen fopen, fclose, fread sind Beispiele für OO-Code in C. Anstelle der privaten Daten in der Klasse arbeiten sie mit der FILE-Struktur, die zum Einkapseln der Daten verwendet wird, und die C-Funktionen fungieren als Elementklassenfunktionen. http://www.amazon.com/File-Structures-Object-Oriented-Approach-C/dp/0201874016
#include <stdio.h>
typedef struct {
int x;
int z;
} base;
typedef struct {
base;
int y;
int x;
} derived;
void function_on_base( base * a) // here I can pass both pointers to derived and to base
{
printf("Class base [%d]\n",a->x);
printf("Class base [%d]\n",a->z);
}
void function_on_derived( derived * b) // here I must pass a pointer to the derived class
{
printf("Class derived [%d]\n",b->y);
printf("Class derived [%d]\n",b->x);
}
int main()
{
derived d;
base b;
printf("Teste de poliformismo\n");
b.x = 2;
d.y = 1;
b.z = 3;
d.x = 4;
function_on_base(&b);
function_on_base(&d);
function_on_derived(&b);
function_on_derived(&d);
return 0;
}
Die Ausgabe war:
Class base [3]
Class base [1]
Class base [4]
Class derived [2]
Class derived [3]
Class derived [1]
Class derived [4]
so funktioniert es, es ist ein polymorpher Code.
OnkelZeiv hat es am Anfang erklärt.
Aus Wikipedia: In Programmiersprachen und Typentheorie ist Polymorphismus (aus dem Griechischen πολύς, polys, "many, much" und μορφή, morphē, "form, shape") die Bereitstellung einer einzigen Schnittstelle zu Entitäten verschiedener Typen.
Daher würde ich sagen, dass die einzige Möglichkeit, es in C zu implementieren, darin besteht, verschiedene Argumente zusammen mit einer (halb) automatischen Verwaltung von Typinformationen zu verwenden. Zum Beispiel können Sie in C ++ schreiben (Entschuldigung für die Trivialität):
void add( int& result, int a1, int a2 );
void add( float& result, float a1, float a2 );
void add( double& result, double a1, double a2 );
In C ist unter anderem das Beste, was Sie tun können:
int int_add( int a1, int a2 );
float float_add( float a1, fload a2 );
double double_add( double a1, double a2 );
void add( int typeinfo, void* result, ... );
Dann brauchen Sie:
Ich bin mir fast sicher, dass jede andere Implementierung des Polymorphismus genau so aussehen sollte. Die obigen Antworten scheinen stattdessen zu versuchen, die Vererbung mehr als den Polymorphismus anzusprechen!
Um auch die OO-Funktionalität in C zu erstellen, können Sie sich frühere Antworten ansehen.
Aber (wie es in anderen Fragen gestellt wurde, die auf diese umgeleitet wurden), wenn Sie verstehen möchten, was Polymorphismus ist, anhand von Beispielen in C-Sprache. Vielleicht irre ich mich, aber mir fällt nichts ein, das so einfach zu verstehen ist wie die C-Zeiger-Arithmetik. Meiner Meinung nach ist die Zeigerarithmetik in C von Natur aus polymorph . Im folgenden Beispiel erzeugt dieselbe Funktion (Methode in OO), nämlich die Addition (+), abhängig von den Eigenschaften der Eingabestrukturen ein unterschiedliches Verhalten.
Beispiel:
double a*;
char str*;
a=(double*)malloc(2*sizeof(double));
str=(char*)malloc(2*sizeof(char));
a=a+2; // make the pointer a, point 2*8 bytes ahead.
str=str+2; // make the pointer str, point 2*1 bytes ahead.
Haftungsausschluss: Ich bin sehr neu bei C und freue mich sehr darauf, korrigiert zu werden und aus den Kommentaren anderer Benutzer zu lernen oder diese Antwort sogar vollständig zu löschen, falls sie falsch sein sollte. Danke vielmals,
Was ich normalerweise gerne mache, ist, die Strukturen in eine andere zu verpacken, die Metainformationen über die verpackte Klasse enthält, und dann besucherähnliche Funktionslisten zu erstellen, die auf die generische Struktur wirken. Der Vorteil dieses Ansatzes besteht darin, dass Sie die vorhandenen Strukturen nicht ändern müssen und Besucher für jede Teilmenge von Strukturen erstellen können.
Nehmen Sie das übliche Beispiel:
typedef struct {
char call[7] = "MIAO!\n";
} Cat;
typedef struct {
char call[6] = "BAU!\n";
} Dog;
Wir können die 2 Streben in diese neue Struktur einwickeln:
typedef struct {
const void * animal;
AnimalType type;
} Animal;
Der Typ kann ein einfaches int sein, aber lassen Sie uns nicht faul sein:
typedef enum {
ANIMAL_CAT = 0,
ANIMAL_DOG,
ANIMAL_COUNT
} AnimalType;
Es wäre schön, einige Verpackungsfunktionen zu haben:
Animal catAsAnimal(const Cat * c) {
return (Animal){(void *)c, ANIMAL_CAT};
}
Animal dogAsAnimal(const Dog * d) {
return (Animal){(void *)d, ANIMAL_DOG};
}
Jetzt können wir unseren "Besucher" definieren:
void catCall ( Animal a ) {
Cat * c = (Cat *)a.animal;
printf(c->call);
}
void dogCall ( Animal a ) {
Dog * d = (Dog *)a.animal;
printf(d->call);
}
void (*animalCalls[ANIMAL_COUNT])(Animal)={&catCall, &dogCall};
Dann ist die tatsächliche Verwendung:
Cat cat;
Dog dog;
Animal animals[2];
animals[0] = catAsAnimal(&cat);
animals[1] = dogAsAnimal(&dog);
for (int i = 0; i < 2; i++) {
Animal a = animals[i];
animalCalls[a.type](a);
}
Der Nachteil dieses Ansatzes besteht darin, dass Sie die Strukturen jedes Mal umbrechen müssen, wenn Sie sie als generischen Typ verwenden möchten.