80 votes

Comment puis-je simuler un polymorphisme de style OO en C?

Est-il un moyen d'écrire OO-comme le code dans l' C langage de programmation?


Voir aussi:

Trouvé en faisant une recherche sur "[c] oo".

65voto

UncleZeiv Points 9033

Le premier compilateur C++ ("C with classes") serait effectivement générer du code C, donc c'est certainement faisable.

Fondamentalement, votre classe de base est une structure (struct); dérivés des structures doit inclure la base struct à la première position, de sorte qu'un pointeur vers le "dérivé de" struct sera également un pointeur valide à la base de struct.

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

Les fonctions peuvent être une partie de la structure de pointeurs de fonction, de sorte qu'une syntaxe du type p->call(p) devient possible, mais vous avez toujours explicitement de passer un pointeur vers la structure à la fonction elle-même.

56voto

Peter Štibraný Points 17507

Approche commune est de définir la structure avec des pointeurs de fonctions. Ceci définit les "méthodes" qui peut être appelé à n'importe quel type. Sous-types, puis de définir leurs propres fonctions dans cette structure commune, et de le retourner.

Par exemple, dans le noyau linux, il est struct:

struct inode_operations {
    int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
    struct dentry * (*lookup) (struct inode *,struct dentry *, 
                               struct nameidata *);
    ...
};

Chaque enregistré type de système de fichiers enregistre alors ses propres fonctions pour create, lookup, et d'autres fonctions. Reste du code peut que l'utilisation de générique inode_operations:

struct inode_operations   *i_op;
i_op -> create(...);

32voto

C++ n'est pas loin de C.

Les Classes sont des structures avec une cachée pointeur vers un tableau de pointeurs de fonction appelée VTable. La Vtable est statique. Lorsque les types de point de Vtables avec la même structure, mais où les pointeurs point à l'autre de la mise en œuvre, vous obtenez le polymorphisme.

Il est recommandé d'encapsuler les appels logique en fonction de prendre la struct comme paramètre pour éviter l'encombrement de code.

Vous devez également encapsulcte structures de l'instanciation et l'initialisation des fonctions (c'est l'équivalent d'une C++ constructeur) et de suppression (destructeur en C++). Ce sont de bonnes pratique de toute façon.

typedef struct
{
   int (*SomeFunction)(TheClass* this, int i);
   void (*OtherFunction)(TheClass* this, char* c);
} VTable;

typedef struct
{
   VTable* pVTable;
   int member;

} TheClass;

Pour appeler la méthode:

int CallSomeFunction(TheClass* this, int i)
{
  (this->SomeFunction)(this, i);
}

18voto

red_hax0r Points 41

J'ai regardé les réponses de tout le monde et j'ai trouvé ceci:

 #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;
}
 

J'espère que cela répond à quelques questions.

12voto

L'annexe B de l'article Ouverte Objet Réutilisable Modèles, par Ian Piumarta et Alessandro Warth de VPRI est une implémentation d'un modèle d'Objet en C de GNU, environ 140 lignes de code. C'est une histoire fascinante !

Voici le cache de la version de la macro qui envoie des messages à des objets, à l'aide d'une extension GNU C (déclaration de l'expression):

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); \ 
}) 

Dans le même doc, avoir un regard sur l' object, vtable, vtable_delegated et symbol des structures, et l' _bind et vtable_lookup fonctions.

Cheers!

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X