En supposant que je doive utiliser C (pas de C ++ ou de compilateurs orientés objet) et que je n'ai pas d'allocation de mémoire dynamique, quelles sont les techniques que je peux utiliser pour implémenter une classe, ou une bonne approximation d'une classe? Est-ce toujours une bonne idée d'isoler la «classe» dans un fichier séparé? Supposons que nous puissions préallouer la mémoire en supposant un nombre fixe d'instances, ou même en définissant la référence à chaque objet comme une constante avant la compilation. N'hésitez pas à faire des hypothèses sur le concept de POO que je devrai implémenter (cela variera) et à suggérer la meilleure méthode pour chacun.
Restrictions:
- Je dois utiliser C et non une POO car j'écris du code pour un système embarqué, et le compilateur et la base de code préexistante sont en C.
- Il n'y a pas d'allocation de mémoire dynamique parce que nous n'avons pas assez de mémoire pour supposer raisonnablement que nous ne manquerons pas si nous commençons à l'allouer dynamiquement.
- Les compilateurs avec lesquels nous travaillons n'ont aucun problème avec les pointeurs de fonction
apr_
et GLib les préfixe avecg_
pour créer un espace de noms) et d'autres facteurs d'organisation sans POO. Si vous restructurez de toute façon l'application, j'envisagerais de passer un peu de temps à essayer de créer une structure procédurale plus durable.Réponses:
Cela dépend de l'ensemble de fonctionnalités "orienté objet" exact que vous souhaitez avoir. Si vous avez besoin de choses comme la surcharge et / ou des méthodes virtuelles, vous devez probablement inclure des pointeurs de fonction dans les structures:
Cela vous permettrait d'implémenter une classe, en "héritant" de la classe de base, et en implémentant une fonction appropriée:
Cela nécessite bien sûr que vous implémentiez également un constructeur, qui s'assure que le pointeur de fonction est correctement configuré. Normalement, vous alloueriez dynamiquement de la mémoire pour l'instance, mais vous pouvez également laisser l'appelant le faire:
Si vous voulez plusieurs constructeurs différents, vous devrez "décorer" les noms des fonctions, vous ne pouvez pas avoir plus d'une
rectangle_new()
fonction:Voici un exemple de base montrant l'utilisation:
J'espère que cela vous donne au moins quelques idées. Pour un framework orienté objet réussi et riche en C, consultez la bibliothèque GObject de glib .
Notez également qu'il n'y a pas de "classe" explicite modélisée ci-dessus, chaque objet a ses propres pointeurs de méthode qui sont un peu plus flexibles que ce que vous trouverez généralement en C ++. En outre, cela coûte de la mémoire. Vous pouvez vous en éloigner en insérant les pointeurs de méthode dans une
class
structure et en inventant un moyen pour chaque instance d'objet de référencer une classe.la source
const ShapeClass *
ouconst void *
comme arguments? Il semblerait que ce dernier soit un peu plus gentil en héritage, mais je peux voir les arguments dans les deux sens ...float (*computeArea)(const ShapeClass *shape);
dire queShapeClass
c'est un type inconnu.struct
références elles-mêmes, il nécessite une déclaration avant d' être défini. Ceci est expliqué avec un exemple ici dans la réponse de Lundin . La modification de l'exemple pour inclure la déclaration de transfert devrait résoudre votre problème;typedef struct ShapeClass ShapeClass; struct ShapeClass { float (*computeArea)(const ShapeClass *shape); };
J'ai dû le faire une fois aussi pour mes devoirs. J'ai suivi cette approche:
Un exemple simple serait celui-ci:
la source
typedef struct Queue Queue;
.Si vous ne voulez qu'une seule classe, utilisez un tableau de
struct
s comme données "objets" et passez-leur des pointeurs vers les fonctions "membres". Vous pouvez utilisertypedef struct _whatever Whatever
avant de déclarerstruct _whatever
pour masquer l'implémentation du code client. Il n'y a aucune différence entre un tel "objet" et l'FILE
objet de bibliothèque standard C.Si vous voulez plus d'une classe avec héritage et fonctions virtuelles, il est courant d'avoir des pointeurs vers les fonctions en tant que membres de la structure, ou un pointeur partagé vers une table de fonctions virtuelles. La bibliothèque GObject utilise à la fois ceci et l'astuce typedef, et est largement utilisée.
Il y a aussi un livre sur les techniques de ce disponible en ligne - Programmation orientée objet avec C ANSI .
la source
vous pouvez jeter un oeil à GOBject. c'est une bibliothèque OS qui vous donne une manière verbeuse de faire un objet.
http://library.gnome.org/devel/gobject/stable/
la source
Interfaces et implémentations C: techniques de création de logiciels réutilisables , David R. Hanson
Ce livre fait un excellent travail pour couvrir votre question. Il fait partie de la série Addison Wesley Professional Computing.
Le paradigme de base est quelque chose comme ceci:
la source
Je vais donner un exemple simple de la façon dont la POO doit être faite en C. Je me rends compte que cette thead date de 2009 mais j'aimerais quand même l'ajouter.
Le concept de base consiste à placer la «classe héritée» en haut de la structure. De cette façon, accéder aux 4 premiers octets de la structure accède également aux 4 premiers octets de la 'classe héritée' (en supposant des optimisations non folles). Désormais, lorsque le pointeur de la structure est converti en «classe héritée», la «classe héritée» peut accéder aux «valeurs héritées» de la même manière qu'elle accède normalement à ses membres.
Ceci et quelques conventions de dénomination pour les constructeurs, les destructeurs, les fonctions d'allocation et de désallocation (je recommande init, clean, new, free) vous feront un long chemin.
Comme pour les fonctions virtuelles, utilisez des pointeurs de fonction dans la structure, éventuellement avec Class_func (...); wrapper aussi. Comme pour les modèles (simples), ajoutez un paramètre size_t pour déterminer la taille, exigez un pointeur void *, ou exigez un type «classe» avec juste la fonctionnalité qui vous intéresse. (par exemple int GetUUID (Object * self); GetUUID (& p);)
la source
Utilisez a
struct
pour simuler les données membres d'une classe. En termes de portée de méthode, vous pouvez simuler des méthodes privées en plaçant les prototypes de fonction privée dans le fichier .c et les fonctions publiques dans le fichier .h.la source
la source
Dans votre cas, la bonne approximation de la classe pourrait être un ADT . Mais ce ne sera toujours pas la même chose.
la source
Ma stratégie est:
Quelqu'un voit-il des problèmes, des trous, des pièges potentiels ou des avantages / inconvénients cachés à l'une ou l'autre variante de cette approche? Si je réinvente une méthode de conception (et je suppose que je dois l'être), pouvez-vous m'en indiquer le nom?
la source
Voir aussi cette réponse et celle-ci
C'est possible. Cela semble toujours être une bonne idée à l'époque, mais après cela, cela devient un cauchemar de maintenance. Votre code est jonché de morceaux de code liant tout ensemble. Un nouveau programmeur aura beaucoup de problèmes à lire et à comprendre le code si vous utilisez des pointeurs de fonction car il ne sera pas évident de savoir quelles fonctions sont appelées.
Le masquage des données avec les fonctions get / set est facile à implémenter en C mais arrêtez-vous là. J'ai vu plusieurs tentatives dans l'environnement embarqué et à la fin c'est toujours un problème de maintenance.
Puisque vous êtes tous prêts à avoir des problèmes de maintenance, j'éviterais.
la source
Mon approche serait de déplacer les fonctions
struct
et toutes les fonctions principalement associées vers un (des) fichier (s) source (s) séparé (s) afin qu'il puisse être utilisé "de manière portative".En fonction de votre compilateur, vous pourrez peut- être inclure des fonctions dans le
struct
, mais c'est une extension très spécifique au compilateur, et n'a rien à voir avec la dernière version du standard que j'utilise régulièrement :)la source
Le premier compilateur C ++ était en fait un préprocesseur qui traduisait le code C ++ en C.
Il est donc très possible d'avoir des classes en C. Vous pourriez essayer de déterrer un ancien préprocesseur C ++ et voir quel genre de solutions il crée.
la source
cfront
; il a rencontré des problèmes lorsque des exceptions ont été ajoutées à C ++ - la gestion des exceptions n'est pas triviale.GTK est entièrement construit sur C et utilise de nombreux concepts de POO. J'ai lu le code source de GTK et il est assez impressionnant et certainement plus facile à lire. Le concept de base est que chaque «classe» est simplement une structure et des fonctions statiques associées. Les fonctions statiques acceptent toutes la structure "instance" comme paramètre, font tout ce dont elles ont besoin et renvoient des résultats si nécessaire. Par exemple, vous pouvez avoir une fonction "GetPosition (CircleStruct obj)". La fonction creuserait simplement dans la structure, extrairait les numéros de position, construirait probablement un nouvel objet PositionStruct, collerait le x et y dans le nouveau PositionStruct et le retournerait. GTK implémente même l'héritage de cette manière en incorporant des structures dans des structures. assez intelligent.
la source
Voulez-vous des méthodes virtuelles?
Sinon, vous définissez simplement un ensemble de pointeurs de fonction dans la structure elle-même. Si vous affectez tous les pointeurs de fonction à des fonctions C standard, vous pourrez appeler des fonctions à partir de C dans une syntaxe très similaire à celle que vous feriez sous C ++.
Si vous voulez avoir des méthodes virtuelles, cela devient plus compliqué. Fondamentalement, vous devrez implémenter votre propre VTable à chaque structure et affecter des pointeurs de fonction à la VTable en fonction de la fonction appelée. Vous auriez alors besoin d'un ensemble de pointeurs de fonction dans la structure elle-même qui à son tour appelle le pointeur de fonction dans le VTable. C'est essentiellement ce que fait C ++.
TBH cependant ... si vous voulez ce dernier, il vaut probablement mieux trouver un compilateur C ++ que vous pouvez utiliser et recompiler le projet. Je n'ai jamais compris l'obsession du C ++ non utilisable en embarqué. Je l'ai utilisé plusieurs fois et cela fonctionne est rapide et n'a pas de problèmes de mémoire. Bien sûr, vous devez être un peu plus prudent dans ce que vous faites, mais ce n'est vraiment pas si compliqué.
la source
C n'est pas un langage POO, comme vous le faites remarquer à juste titre, il n'y a donc pas de moyen intégré d'écrire une vraie classe. Le mieux est de regarder les structures et les pointeurs de fonction , ceux-ci vous permettront de construire une approximation d'une classe. Cependant, comme C est procédural, vous voudrez peut-être envisager d'écrire plus de code de type C (c'est-à-dire sans essayer d'utiliser des classes).
De plus, si vous pouvez utiliser C, vous pouvez probablement utiliser C ++ et obtenir des classes.
la source