Définir des structures opaques
Fait partie de la section Object Oriented Programming du Journey C de Coddy — leçon 18 sur 61.
Maintenant que vous comprenez le concept de pointeurs opaques, voyons comment compléter le modèle. L'en-tête déclare qu'un type existe, mais le fichier source est l'endroit où vous définissez ce qu'il contient réellement.
Voici la configuration complète. Tout d'abord, l'en-tête avec seulement la déclaration anticipée :
// counter.h
#ifndef COUNTER_H
#define COUNTER_H
typedef struct Counter Counter; // Type incomplet
Counter *create_counter(int start);
void free_counter(Counter *c);
#endif
Ensuite, le fichier source contient la définition réelle de la structure :
// counter.c
#include "counter.h"
#include <stdlib.h>
struct Counter {
int value; // Masqué de l'extérieur !
int step;
};
Counter *create_counter(int start) {
Counter *c = malloc(sizeof(Counter));
c->value = start;
c->step = 1;
return c;
}
void free_counter(Counter *c) {
free(c);
}
Le point clé : struct Counter { ... } apparaît uniquement dans le fichier .c. Tout fichier qui inclut counter.h sait que Counter existe mais ne peut pas voir value ou step. Tenter d'écrire c->value dans main.c provoquerait une erreur de compilation — les membres sont véritablement invisibles.
Cette séparation est le fondement de l'encapsulation en C. L'étape suivante consiste à fournir un accès contrôlé via des fonctions getter et setter.
Défi
FacileConstruisons un module Timer qui illustre le modèle du pointeur opaque (opaque pointer pattern). Vous allez vous entraîner à masquer la définition de la structure à l'intérieur du fichier source, rendant ses membres internes complètement invisibles pour tout code utilisant votre module.
Vous allez créer trois fichiers :
timer.h: C'est votre interface publique. Déclarez un typeTimeropaque en utilisant uniquement une déclaration anticipée (forward declaration) — pas de corps de structure ici ! Déclarez également deux fonctions :create_timer(prend un entier pour les secondes et retourne un pointeur Timer) etfree_timer(prend un pointeur Timer et libère les ressources). Utilisez des gardes d'inclusion avec le symboleTIMER_H.timer.c: C'est ici que réside le secret. Définissez la structure réellestruct Timeravec deux membres cachés :int secondsetint running(utilisez 1 pour en cours d'exécution, 0 pour arrêté). Implémentez votre constructeur pour allouer la structure, initialisersecondsavec la valeur fournie, et définirrunningà 1. Implémentez le destructeur pour libérer la mémoire.main.c: Utilisez votre module Timer pour créer un minuteur et confirmer qu'il a été créé avec succès. Puisque les membres de la structure sont cachés, vous ne pouvez pas y accéder directement — c'est tout l'intérêt ! Affichez simplement un message confirmant la création, puis libérez le minuteur et affichez un message de nettoyage.
Vous recevrez une entrée : le nombre de secondes (un entier) pour initialiser le minuteur.
Dans votre fichier principal, créez un minuteur avec la valeur de secondes fournie. Affichez un message de création, puis libérez le minuteur et affichez une confirmation.
Affichez la sortie dans ce format :
Timer created
Timer freedL'idée clé ici est que main.c peut manipuler des pointeurs Timer mais n'a aucun moyen d'accéder directement à seconds ou running. Tenter d'écrire t->seconds dans main.c provoquerait une erreur de compilation car le corps de la structure n'est défini que dans timer.c. C'est une véritable encapsulation — les détails d'implémentation sont complètement cachés aux utilisateurs de votre module.
Aide-mémoire
Le motif du pointeur opaque sépare la déclaration d'un type de sa définition, masquant ainsi les détails d'implémentation.
Le fichier d'en-tête contient uniquement une déclaration anticipée :
// counter.h
#ifndef COUNTER_H
#define COUNTER_H
typedef struct Counter Counter; // Type incomplet
Counter *create_counter(int start);
void free_counter(Counter *c);
#endif
Le fichier source contient la définition réelle de la structure :
// counter.c
#include "counter.h"
#include <stdlib.h>
struct Counter {
int value; // Masqué de l'extérieur
int step;
};
Counter *create_counter(int start) {
Counter *c = malloc(sizeof(Counter));
c->value = start;
c->step = 1;
return c;
}
void free_counter(Counter *c) {
free(c);
}
La définition de la structure n'apparaît que dans le fichier .c. Les fichiers qui incluent l'en-tête savent que le type existe mais ne peuvent pas accéder directement à ses membres. Cela offre une véritable encapsulation en C.
Essayez vous-même
#include <stdio.h>
#include "timer.h"
int main() {
int seconds;
scanf("%d", &seconds);
// TODO: Créer un minuteur avec la valeur de secondes fournie
// TODO: Afficher "Timer created"
// TODO: Libérer le minuteur
// TODO: Afficher "Timer freed"
return 0;
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Object Oriented Programming
1Bases de la programmation modulaire
Fichiers d'en-têteGardes d'inclusionFichiers sourcesFonctions statiquesRécapitulatif : Calculatrice modulaire4Encapsulation
Concept des pointeurs opaquesDéfinir des structures opaquesGetters et settersValidation dans les settersRécapitulatif : La boîte secrète2Objets et méthodes
Structs comme objetsLe pointeur 'Self'Rigueur du mot-clé constPointeur vs ValeurMéthodes utilitairesRécapitulatif : Point Manager5Projet : Compte bancaire simple
Configuration du projetImplémentation du compte8Polymorphisme
Pointeurs de fonctions dans les structuresSimulation de méthodesLe concept d'interfaceImplémentation d'interfacesItération polymorphiqueRécapitulatif : Greeter11Patrons de conception en C
Patron SingletonPatron FabriquePatron ItérateurRécapitulatif : Logger Factory3Cycle de vie des objets
Pattern de constructeurPattern de destructeurInitialisation sur la pileCopie profondeRécapitulatif : String Wrapper6Héritage par composition
Imbrication de structLa règle du premier membreAccès aux membres parentsUpcastingRécapitulatif : Hiérarchie des formes9Projet : Dessinateur de formes
Aperçu du projetImplémentation du cercle