3 votes

Comment hériter d'une structure dans un simple C

Je travaille en C simple (projet embarqué, peu de mémoire) et j'ai une structure

typedef struct 
{
   int x;
   int y;
   int z;
   float angle;
   float angle1;
   float angle2;
} Kind1;

Il y a des cas où j'ai besoin de tous les champs, et d'autres où je n'ai besoin que de x, y et angle.

En C++, je créerais une classe de base avec ces 3 champs, j'hériterais d'une autre classe avec 3 autres champs et j'instancierais l'une ou l'autre selon les besoins. Comment puis-je émuler ce comportement en C ordinaire ?

Je sais que je peux faire quelque chose comme

typedef struct 
{
   int x;
   int y;
   float angle;
} Kind1;

typedef struct
{ 
   Kind1 basedata;
   int z;
   float angle2;
   float angle3;
}  Kind2;

mais alors je ne peux pas passer le pointeur à Kind2 où un pointeur à Kind1 est demandé.

Je sais qu'il est possible d'effectuer un typage et de décaler le pointeur, mais je me demande s'il existe une meilleure méthode, plus sûre.

5voto

Luchian Grigore Points 136646

Je sais qu'il est possible de faire un typecast et de décaler le pointeur.

Pas nécessairement :

void foo(Kind1*);
struct Kind2
{ 
   Kind1 basedata;
   int z;
   float angle2;
   float angle3;
}

//...
Kind2 k;
foo(&(k.basedata));

2voto

Paul R Points 104036

Vous pouvez le faire comme vous le feriez en C++ :

struct Kind1
{
   int x;
   int y;
   float angle;
}

struct Kind2
{ 
   int z;
   float angle2;
   float angle3;
}

struct Kind
{
    Kind1 k1;
    Kind2 k2;
}

2voto

Joachim Pileborg Points 121221

Ce n'est pas possible en C simple, le langage ne possède pas de telles fonctionnalités. Cependant, vous pouvez simplifier les typescasts et offsets avec des macros de pré-processeur.

2voto

Kevin Cox Points 725

Les exemples ci-dessous partent de ces définitions.

struct base { char c; } *a_base;
struct sub  { struct base b; int i; } *a_sub;

Votre "exemple" est en fait la solution correcte (la plus simple).

mais alors je ne peux pas passer le pointeur à Kind2 où un pointeur à Kind1 est demandé.

Si, vous le pouvez. Ce qui suit est tiré de la norme C11, mais les révisions précédentes présentaient les mêmes garanties [citation nécessaire].

n1570 6.7.1.1.15

... Un pointeur vers un objet structure, convenablement converti, pointe vers son membre initial (ou si ce membre est un champ de bits, alors vers l'unité dans laquelle il réside), et vice versa. Il peut y avoir un remplissage sans nom à l'intérieur d'un objet structure, mais pas à son début.

Par conséquent, (struct base*)a_sub == &a_sub->b y ((struct base*)a_sub)->c == a_sub->b.c

Ainsi, tant que votre "super-structure" est le premier membre de votre "sous-structure", vous pouvez traiter l'une comme l'autre. lors d'un accès par référence . En gros, ne faites pas ça.

void copy(struct base *from, struct base *to)
{
    *to = *from; // Oops, you just lost half your object.
}

0voto

wallyk Points 33150

Dans le cas d'une mémoire limitée, préservez la santé mentale en déclarant deux structs différents :

struct Kind1
{
   int x;
   int y;
   float angle;
}

struct Kind2
{ 
   int x;
   int y;
   int z;
   float angle;
   float angle2;
   float angle3;
}

J'ai eu une fois à travailler sur un code qui utilisait une union et un préprocesseur #define pour rendre le code regardez plus lisible. Cependant, cela mène rapidement à la folie. Si les deux structures sont effectivement gérées comme une sous-classe, la réorganisation des champs est le moindre mal :

struct Kind1
{
   int x;
   int y;
   float angle;
}

struct Kind2
{ 
   int x;
   int y;
   float angle;
   // extended data:
   int z;
   float angle2;
   float angle3;
}

pour autant qu'ils soient utilisés avec précaution lors du moulage. Cependant, si quelque chose ne va pas, il devrait vraiment y avoir un contrôle du nom de la version de débogage pour prouver que tout est fait correctement.

struct Kind1
{
   char variant[6];  // initialized to "kind1" 
   int x;
   int y;
   float angle;
}

struct Kind2
{ 
   char variant[6];  // initialized to "kind2" 
   int x;
   int y;
   float angle;
   // extended data:
   int z;
   float angle2;
   float angle3;
}

function_expecting_kind2 (struct kind2 *p)
{
     if (strcmp (p->variant, "kind2"))
           error ("function expecting kind2 got '%s' instead", p->variant);
     ...
}

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