OVH Cloud OVH Cloud

"héritage" à la C

20 réponses
Avatar
Nicolas Aunai
salut,


j'ai fait ma version de liste chainée nommée 'list', et j'ai décidé de
faire un autre "objet" 'vect' qui serait une liste chainée list avec
des caractéristiques en plus...

en gros, c'est une structure avec une 'list' dedans et d'autres
choses....


maintenant, l'utilisateur est en droit d'attendre que le 'vect'
présente les même possibilités que la 'list', c'est à dire
ajouter/retirer des élements, rechercher, se déplacere etc...


cependant, la 'list' du 'vect' est présente dans la structure sous
forme de pointeur, et cette structure n'est PAS accessible a
l'utilisateur (en quelque sorte, elle est private)


ma question est donc : comment présenter dans l'interface du 'vect' les
caractéristiques de la 'list' sans :

- refaire toute l'interface 'list' dans le module 'vect'
- donner à l'utilisateur l'accès au pointeur list* de la structure
vect.

(car une solution simple serait de faire une sorte d'accesseur :

list_t *vect_GetList(vect_t *v)
{
return v->pl; //donne la main sur la liste
}

mais ça serait contraire à l'esprit du code qui veut cacher tous ces
détails à l'utilisateur, pour ne lui présenter qu'une interface fixe et
déterminée.)


merci pour vos suggestions.
a+

--
Nico,
http://astrosurf.com/nicoastro
messenger : nicolas_aunai@hotmail.com

10 réponses

1 2
Avatar
cedric
bruno modulix wrote:
A par ca tu as raison, les accesseurs c'est Mal.


Chapitre et verset, svp ?


Chapitre 1er, verset concernant l'encapsulation.


Avatar
cedric
DINH Viêt Hoà wrote:
((list *) objet) donnera un objet list manipulable par les fonctions de
liste.


L'ennuis de ce cast, c'est qu'on ne peut pas faire d'héritage multiple avec.

Avatar
DINH Viêt Hoà

DINH Viêt Hoà wrote:
((list *) objet) donnera un objet list manipulable par les fonctions de
liste.


L'ennuis de ce cast, c'est qu'on ne peut pas faire d'héritage multiple avec.


heu ... ouais, l'héritage multiple, c'est super peu utilisé en pratique,
me semble-t-il.

et si on veut faire des choses aussi poussée que cela, il vaut mieux
passer au C++.

--
DINH V. Hoa,

"mais tu es perspicace, on dirait Sammy dans Scooby-Doo"


Avatar
Nicolas Aunai
DINH Viêt Hoà a pensé très fort :

((list *) objet) donnera un objet list manipulable par les fonctions de
liste.



bon beh j'ai une question a propos du cast, voici l'exemple :

soit le structure :

typedef struct
{
list_t *pl; /*la liste de données*/
/*[...]*/
}vect_t;

mon nouveau type de liste chainée, héritant de mon type de base...

j'ai un constructeur pour 'vect_t' :

vect_t *vect_new()
{
/*printf("allocation memoire du vect....");*/
vect_t *v = malloc(sizeof(vect_t));
if(v)
{
/*printf(" okn");*/
/*printf("allocation memoire de la liste...");*/
v->pl = list_new();
if(v->pl)
{
/*printf(" okn");*/
v->add = NULL;
}
else
return NULL;
}
/*printf("nb = %dn",list_count(v->pl));*/
return v;
}


bon, ensuite, imaginons la fonction 'list_count()' qui renvoit le
nombre d'items d'un objet 'list_t' (en fait un simple return d'un
membre 'nb' de list_t).
ce membre 'nb' est initialisé à 0 par le constructeur de 'list_t' et le
printf juste avant le return v, me permet de vérifier que nb est bien
initialisé à 0.


jusque là, pas de probleme...

imaginons à présent que je définisse une fonction :

int vect_count(vect_t *pv)
{
return list_count(pv->pl);
}

qui me retourne le nombre d'éléments d'un vecteur... là c'est ok, après
la construction, un appel a cette fonction me renvoie bien 0.

en revanche, si je fais :

int vect_count(vect_t *pv)
{
return list_count((list*)pv);
}

ça ne fonctionne plus et me renvoie qqch du style 407000

que se passe-t-il ?

--
Nico,
http://astrosurf.com/nicoastro
messenger :

Avatar
DINH Viêt Hoà

DINH Viêt Hoà a pensé très fort :

((list *) objet) donnera un objet list manipulable par les fonctions de
liste.



bon beh j'ai une question a propos du cast, voici l'exemple :

soit le structure :

typedef struct
{
list_t *pl; /*la liste de données*/
/*[...]*/
}vect_t;


je crois que tu cherches :

typedef struct {
list_t pl; /* ce n'est pas un pointeur */
/* [...] */
} vect_t;

essaie d'imaginer la représentation en mémoire des données,
dans ce que tu donnes, tu as un pointeur comme premier élément
puis tes définitions spécifiques.
Dans ce que je te donne, tu as la copie de la représentation mémoire
de list_t puis les définitions spécifiques.

je conseillerai du coup plutôt des constructeurs du style :
vect_t * vect_new() suivi de vect_init(vect_t *)

list_t * list_new() suivi de list_init(list_t *)

vect_new() étant un malloc()
et vect_init() :

int vect_init(vect_t * v)
{
int r;

r = list_init(v);
if (r != NO_ERROR)
return r;

/* initialisation spécifiques */

return NO_ERROR;
}

--
DINH V. Hoa,

"mais tu es perspicace, on dirait Sammy dans Scooby-Doo"


Avatar
Nicolas Aunai
DINH Viêt Hoà avait prétendu :

typedef struct {
list_t pl; /* ce n'est pas un pointeur */
/* [...] */
} vect_t;

essaie d'imaginer la représentation en mémoire des données,
dans ce que tu donnes, tu as un pointeur comme premier élément
puis tes définitions spécifiques.
Dans ce que je te donne, tu as la copie de la représentation mémoire
de list_t puis les définitions spécifiques.



quel interêt à ce changement ?
qu'est-ce qui ne va pas avec mon pointeur *pl ?

et en plus tu me propose de séparer allocation et initialisation, soit
deux fonctions au lieu d'une pour l'interface, j'ai décidément du mal a
voir un intérêt a tout ça :/

--
Nico,
http://astrosurf.com/nicoastro
messenger :

Avatar
Yves ROMAN

DINH Viêt Hoà avait prétendu :

typedef struct {
list_t pl; /* ce n'est pas un pointeur */
/* [...] */
} vect_t;

essaie d'imaginer la représentation en mémoire des données,
dans ce que tu donnes, tu as un pointeur comme premier élément
puis tes définitions spécifiques.
Dans ce que je te donne, tu as la copie de la représentation mémoire
de list_t puis les définitions spécifiques.


quel interêt à ce changement ?
qu'est-ce qui ne va pas avec mon pointeur *pl ?



Si tu mets un pointeur, le mécanisme du cast ne marchera pas.
Par exemple dans :
int vect_count(vect_t *pv)
{
return list_count((list_t*)pv);
}
car il suppose qu'à l'adresse contenu dans pv (donc au debut d'un 'vect_t') il y
a un 'list_t'. Mais avec ton pointeur il y a un 'list_t *'.
Si, dans 'list_t' le compteur est un 'int' au debut de la structure,
'list_count' va essayer de traduire l'adressee contenu dans pv->pl en int, d'ou
un resultat bizarre au mieux.

Pour que le cast marche, il faut que la structure de données 'vect_t' commence
par une structure de données 'list_t'


Avatar
cedric
DINH Viêt Hoà wrote:
heu ... ouais, l'héritage multiple, c'est super peu utilisé en pratique,
me semble-t-il.


Pour nous (en C), la même construction vaut aussi pour les relations
"implémente" que pour les relations d'héritage. On a donc rapidement
besoin de "l'héritage multiple".

Avatar
Bruno Desthuilliers
cedric wrote:
bruno modulix wrote:

A par ca tu as raison, les accesseurs c'est Mal.



Chapitre et verset, svp ?


Chapitre 1er, verset concernant l'encapsulation.


<meta>x-post et fu2 fr.comp.object</meta>

L'encapsulation n'interdit pas de lire ou modifier les *propriétés* d'un
objet. Elle recommande de ne pas accéder directement à
*l'implémentation* de l'objet. Ce qui est différent.

Bruno



Avatar
Miguel Moquillon
Bonjour,

je prend en vol la discussion à partir de fr.comp.objet et poste à
partir du post de Bruno. Mon post détaille la réponse de Bruno.
(j'avais oublié de crossposté sur fr.comp.lang.c, désolé. Je le réécri à
nouveau)

L'encapsulation n'interdit pas de lire ou modifier les *propriétés* d'un
objet. Elle recommande de ne pas accéder directement à
*l'implémentation* de l'objet. Ce qui est différent.


Un rappel:
un objet est défini par des propriétés. Celles-ci peuvent être découpées
en deux catégories :
- les attributs qui qualifient l'objet ; attention, un attribut n'est
pas nécessairement un champ de donnée (ceci relève de l'implémentation)
- les opérations qui décrivent les services et le comportement éventuel
de l'objet.

Maintenant, dans les langages basé sur le C, les attributs et les
comportement sont représentées par des fonctions (unification de la
représentation des propriétés) ; les "getteurs" représentant les
attributs. Le pb vient des "setteurs". Dans une bonne conception, les
attributs ne devraient pas être modifiées directement (donc par des
"setteurs" en tant que tel) mais à la suite d'appel de services ou de
comportements. Lorsqu'il y a changement par un "setter" d'une
propriété, celle-ci est elle bien un attribut ? Souvent non, mais
plutôt un drapeau dont la valeur est "mise à jour" (donc le sens
"update" et non "setting") par un service !
Le problème vient que bcp mélange champ de données et attributs.

Miguel

1 2