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.)
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
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
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
((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"
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"
((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"
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 :/
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 :/
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 :/
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'
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'
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'
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".
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".
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".
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
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.
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
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
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.
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.