Vous pouvez certainement écrire C dans un style (un peu) orienté objet.
L'encapsulation peut être effectuée en conservant les définitions de vos structures dans le fichier .c plutôt que dans l'en-tête associé. Puis le monde extérieur gère vos objets en gardant des pointeurs sur eux, et vous fournir des fonctions acceptant des pointeurs tels que les "méthodes" de vos objets.
comportement Polymorphisme semblable peut être obtenu en utilisant des pointeurs de fonctions, généralement regroupées au sein de « structures d'exploitation », un peu comme la « table de méthode virtuelle » dans votre C++ objets (ou quelque chose comme ça). La structure ops peut également inclure d'autres éléments tels que constantes dont la valeur est spécifique à une "sous-classe" donnée. La structure "parent" peut conserver une référence à des données spécifiques à l'op via un pointeur générique void*
. Bien sûr, la "sous-classe" pourrait répéter le modèle pour plusieurs niveaux de l'héritage.
Ainsi, dans l'exemple ci-dessous, struct printer
est semblable à une classe abstraite, qui peut être « dérivée » en complétant une structure pr_ops
, et fournir une fonction de constructeur d'emballage pr_create()
. Chaque sous-type aura sa propre structure qui sera "ancrée" à l'objet struct printer
via le pointeur générique data
. Ceci est démontré par le sous-type fileprinter
. On pourrait imaginer une imprimante GUI ou socket, qui serait manipulée indépendamment du reste du code comme référence struct printer *
.
printer.h:
struct pr_ops {
void (*printline)(void *data, const char *line);
void (*cleanup)(void *data);
};
struct printer *pr_create(const char *name, const struct output_ops *ops, void *data);
void pr_printline(struct printer *pr, const char *line);
void pr_delete(struct printer *pr);
printer.c:
#include "printer.h"
...
struct printer {
char *name;
struct pr_ops *ops;
void *data;
}
/* constructor */
struct printer *pr_create(const char *name, const struct output_ops *ops, void *data)
{
struct printer *p = malloc(sizeof *p);
p->name = strdup(name);
p->ops = ops;
p->data = data;
}
void pr_printline(struct printer *p, const char *line)
{
char *l = malloc(strlen(line) + strlen(p->name) + 3;
sprintf(l, "%s: %s", p->name, line);
p->ops->printline(p->data, l);
}
void pr_delete(struct printer *p)
{
p->ops->cleanup(p->data);
free(p);
}
Enfin, fileprinter.c:
struct fileprinter {
FILE *f;
int doflush;
};
static void filepr_printline(void *data, const char *line)
{
struct fileprinter *fp = data;
fprintf(fp->f, "%s\n", line);
if(fp->doflush) fflush(fp->f);
}
struct printer *filepr_create(const char *name, FILE *f, int doflush)
{
static const struct ops = {
filepr_printline,
free,
};
struct *fp = malloc(sizeof *fp);
fp->f = f;
fp->doflush = doflush;
return pr_create(name, &ops, fp);
}
Quelle est la raison de le faire? –
J'ai du code C en utilisant principalement des porte-conteneurs. Le projet est arrivé à un point où la substituabilité (une relation «est un») est devenue très souhaitable. –
répond essentiellement à toutes vos questions: [http://stackoverflow.com/questions/415452/object-orientation-in-c/415536#415536](http://stackoverflow.com/questions/415452/object-orientation-in -c/415536 # 415536) – Junier