Je déconseille l'(ab)utilisation du préprocesseur pour essayer de rendre la syntaxe du C plus proche de celle d'un autre langage plus orienté objet. Au niveau le plus basique, on utilise simplement des structs comme objets et on les fait passer par des pointeurs :
struct monkey
{
float age;
bool is_male;
int happiness;
};
void monkey_dance(struct monkey *monkey)
{
/* do a little dance */
}
Pour obtenir des choses comme l'héritage et le polymorphisme, il faut travailler un peu plus dur. Vous pouvez faire de l'héritage manuel en faisant en sorte que le premier membre d'une structure soit une instance de la superclasse, et vous pouvez ensuite faire circuler librement les pointeurs vers les classes de base et dérivées :
struct base
{
/* base class members */
};
struct derived
{
struct base super;
/* derived class members */
};
struct derived d;
struct base *base_ptr = (struct base *)&d; // upcast
struct derived derived_ptr = (struct derived *)base_ptr; // downcast
Pour obtenir le polymorphisme (c'est-à-dire les fonctions virtuelles), vous utilisez des pointeurs de fonction, et éventuellement des tables de pointeurs de fonction, également connues sous le nom de tables virtuelles ou vtables :
struct base;
struct base_vtable
{
void (*dance)(struct base *);
void (*jump)(struct base *, int how_high);
};
struct base
{
struct base_vtable *vtable;
/* base members */
};
void base_dance(struct base *b)
{
b->vtable->dance(b);
}
void base_jump(struct base *b, int how_high)
{
b->vtable->jump(b, how_high);
}
struct derived1
{
struct base super;
/* derived1 members */
};
void derived1_dance(struct derived1 *d)
{
/* implementation of derived1's dance function */
}
void derived1_jump(struct derived1 *d, int how_high)
{
/* implementation of derived 1's jump function */
}
/* global vtable for derived1 */
struct base_vtable derived1_vtable =
{
&derived1_dance, /* you might get a warning here about incompatible pointer types */
&derived1_jump /* you can ignore it, or perform a cast to get rid of it */
};
void derived1_init(struct derived1 *d)
{
d->super.vtable = &derived1_vtable;
/* init base members d->super.foo */
/* init derived1 members d->foo */
}
struct derived2
{
struct base super;
/* derived2 members */
};
void derived2_dance(struct derived2 *d)
{
/* implementation of derived2's dance function */
}
void derived2_jump(struct derived2 *d, int how_high)
{
/* implementation of derived2's jump function */
}
struct base_vtable derived2_vtable =
{
&derived2_dance,
&derived2_jump
};
void derived2_init(struct derived2 *d)
{
d->super.vtable = &derived2_vtable;
/* init base members d->super.foo */
/* init derived1 members d->foo */
}
int main(void)
{
/* OK! We're done with our declarations, now we can finally do some
polymorphism in C */
struct derived1 d1;
derived1_init(&d1);
struct derived2 d2;
derived2_init(&d2);
struct base *b1_ptr = (struct base *)&d1;
struct base *b2_ptr = (struct base *)&d2;
base_dance(b1_ptr); /* calls derived1_dance */
base_dance(b2_ptr); /* calls derived2_dance */
base_jump(b1_ptr, 42); /* calls derived1_jump */
base_jump(b2_ptr, 42); /* calls derived2_jump */
return 0;
}
Et c'est comme ça qu'on fait du polymorphisme en C. Ce n'est pas joli, mais ça fait l'affaire. Il y a quelques problèmes épineux impliquant les casts de pointeurs entre les classes de base et les classes dérivées, qui sont sûrs tant que la classe de base est le premier membre de la classe dérivée. L'héritage multiple est beaucoup plus difficile - dans ce cas, pour passer d'une classe de base à une autre que la première, vous devez ajuster manuellement vos pointeurs en fonction des décalages appropriés, ce qui est vraiment délicat et source d'erreurs.
Une autre chose (délicate) que vous pouvez faire est de changer le type dynamique d'un objet au moment de l'exécution ! Il suffit de lui réassigner un nouveau pointeur de table virtuelle. Vous pouvez même changer sélectivement certaines fonctions virtuelles tout en conservant d'autres, créant ainsi de nouveaux types hybrides. Faites juste attention à créer une nouvelle table virtuelle au lieu de modifier la table virtuelle globale, sinon vous affecterez accidentellement tous les objets d'un type donné.
1 votes
Je peux répondre pour apprendre le D et utiliser le c compatible abi pour les endroits où tu as vraiment besoin du C. digitalmars.com/d
0 votes
Pas vraiment. Je travaille avec des systèmes embarqués qui n'ont qu'un compilateur C à leur disposition.
2 votes
@Dinah : Merci pour le "Voir aussi". Ce billet était intéressant.
1 votes
La question intéressante semble être de savoir pourquoi vous voudriez un pré-processeur pirate de la POO sur C.
3 votes
@Calyth : Je trouve que la POO est utile et "je travaille avec certains systèmes embarqués qui n'ont vraiment qu'un compilateur C à disposition" (d'après ce qui précède). De plus, ne trouvez-vous pas que les bidouillages de préprocesseurs sont intéressants à regarder ?
2 votes
Duplicata possible de Pouvez-vous écrire du code orienté objet en C ?