kanze wrote:Comme tu sais, le langage n'impose pas de vtable:-). Mais en
effet, c'est à peu près comme ça dans toutes les
implémentations que je connais.
Oui. Je n'ai jamais réussi à retenir les solutions
alternatives. Je crois qu'elles ont déjà été évoquées ici
(est-ce smalltak qui utilise un autre mécanisme ?) mais ma
mémoire refuse de s'en souvenir.
Les alternatives sont-elles vraiment moins bonnes, ou bien la
vtable s'est-elle seulement imposée pour des raisons pratiques
(du genre réutilisation du linker C) ?
constructeur de T. (En Java, c'est très facile de se trouver
dans une fonction sur un objet dont le constructeur n'a pas
encore été appelé.)
Ça, je ne le savais pas. Ça me paraît plus que curieux et je
suis content de l'apprendre (j'ai peu eu à développer en Java
et n'ai jamais été confronté à cette situation).
kanze wrote:
Comme tu sais, le langage n'impose pas de vtable:-). Mais en
effet, c'est à peu près comme ça dans toutes les
implémentations que je connais.
Oui. Je n'ai jamais réussi à retenir les solutions
alternatives. Je crois qu'elles ont déjà été évoquées ici
(est-ce smalltak qui utilise un autre mécanisme ?) mais ma
mémoire refuse de s'en souvenir.
Les alternatives sont-elles vraiment moins bonnes, ou bien la
vtable s'est-elle seulement imposée pour des raisons pratiques
(du genre réutilisation du linker C) ?
constructeur de T. (En Java, c'est très facile de se trouver
dans une fonction sur un objet dont le constructeur n'a pas
encore été appelé.)
Ça, je ne le savais pas. Ça me paraît plus que curieux et je
suis content de l'apprendre (j'ai peu eu à développer en Java
et n'ai jamais été confronté à cette situation).
kanze wrote:Comme tu sais, le langage n'impose pas de vtable:-). Mais en
effet, c'est à peu près comme ça dans toutes les
implémentations que je connais.
Oui. Je n'ai jamais réussi à retenir les solutions
alternatives. Je crois qu'elles ont déjà été évoquées ici
(est-ce smalltak qui utilise un autre mécanisme ?) mais ma
mémoire refuse de s'en souvenir.
Les alternatives sont-elles vraiment moins bonnes, ou bien la
vtable s'est-elle seulement imposée pour des raisons pratiques
(du genre réutilisation du linker C) ?
constructeur de T. (En Java, c'est très facile de se trouver
dans une fonction sur un objet dont le constructeur n'a pas
encore été appelé.)
Ça, je ne le savais pas. Ça me paraît plus que curieux et je
suis content de l'apprendre (j'ai peu eu à développer en Java
et n'ai jamais été confronté à cette situation).
donc que la "fonction soit appelée dans le cst ou ailleurs"
est inexact.
Non seulement tu ne connais pas le C++, tu ne veux pas
comprendre.
Jusqu'ici, je n'ai pas vu d'alternatif qui tient la route. La
solution de Java est une source grave d'erreurs, et ne marche
pas dans la pratique. Qu'est-ce que tu proposes d'autre ?
Le problème, d'après mon expérience (concrète), c'est plutôt
avec la façon que fait Java, où on se trouve dans une
fonction membre sur un objet dont le constructeur n'a pas
encore été appelé. C'est une source d'erreurs importante.
Je vois que tes connaissances de Java sont aussi faible que
celles du C++. Essaie de dériver d'une classe de Swing, par
exemple, et de supplanter certaines fonctions virtuelles. Tu te
rétrouves vite dans la fonction sans que le constructeur a été
appelé, et avec toutes les variables membre à null.
hein ? quel rapport ? gérer un delete this au milieu du
destructeur ?
Tiens... Tu n'as jamais entendu parler des exceptions ? Elles
existent pourant, et en Java et en C++. En Java, en cas
d'exception, on laisse tout comme il était, et on espère pour le
mieux. C'est quasiment impossible d'écrire un programme
« correct » dans de tels cas, mais qu'importe.
En C++, en cas d'exception,
on appelle des destructeurs des sous-objets déjà construits.
Qu'est-ce que les instances statiques aient à voir ici.
-- et obtenir une NullPointerException est justement agréable,
Par rapport à quoi ?
La norme laisse la liberté aux implémentations de faire quelque
chose d'intelligent. Mais effectivement, peu le font (VC++, je
crois, mais je crois que c'est le seul).
donc que la "fonction soit appelée dans le cst ou ailleurs"
est inexact.
Non seulement tu ne connais pas le C++, tu ne veux pas
comprendre.
Jusqu'ici, je n'ai pas vu d'alternatif qui tient la route. La
solution de Java est une source grave d'erreurs, et ne marche
pas dans la pratique. Qu'est-ce que tu proposes d'autre ?
Le problème, d'après mon expérience (concrète), c'est plutôt
avec la façon que fait Java, où on se trouve dans une
fonction membre sur un objet dont le constructeur n'a pas
encore été appelé. C'est une source d'erreurs importante.
Je vois que tes connaissances de Java sont aussi faible que
celles du C++. Essaie de dériver d'une classe de Swing, par
exemple, et de supplanter certaines fonctions virtuelles. Tu te
rétrouves vite dans la fonction sans que le constructeur a été
appelé, et avec toutes les variables membre à null.
hein ? quel rapport ? gérer un delete this au milieu du
destructeur ?
Tiens... Tu n'as jamais entendu parler des exceptions ? Elles
existent pourant, et en Java et en C++. En Java, en cas
d'exception, on laisse tout comme il était, et on espère pour le
mieux. C'est quasiment impossible d'écrire un programme
« correct » dans de tels cas, mais qu'importe.
En C++, en cas d'exception,
on appelle des destructeurs des sous-objets déjà construits.
Qu'est-ce que les instances statiques aient à voir ici.
-- et obtenir une NullPointerException est justement agréable,
Par rapport à quoi ?
La norme laisse la liberté aux implémentations de faire quelque
chose d'intelligent. Mais effectivement, peu le font (VC++, je
crois, mais je crois que c'est le seul).
donc que la "fonction soit appelée dans le cst ou ailleurs"
est inexact.
Non seulement tu ne connais pas le C++, tu ne veux pas
comprendre.
Jusqu'ici, je n'ai pas vu d'alternatif qui tient la route. La
solution de Java est une source grave d'erreurs, et ne marche
pas dans la pratique. Qu'est-ce que tu proposes d'autre ?
Le problème, d'après mon expérience (concrète), c'est plutôt
avec la façon que fait Java, où on se trouve dans une
fonction membre sur un objet dont le constructeur n'a pas
encore été appelé. C'est une source d'erreurs importante.
Je vois que tes connaissances de Java sont aussi faible que
celles du C++. Essaie de dériver d'une classe de Swing, par
exemple, et de supplanter certaines fonctions virtuelles. Tu te
rétrouves vite dans la fonction sans que le constructeur a été
appelé, et avec toutes les variables membre à null.
hein ? quel rapport ? gérer un delete this au milieu du
destructeur ?
Tiens... Tu n'as jamais entendu parler des exceptions ? Elles
existent pourant, et en Java et en C++. En Java, en cas
d'exception, on laisse tout comme il était, et on espère pour le
mieux. C'est quasiment impossible d'écrire un programme
« correct » dans de tels cas, mais qu'importe.
En C++, en cas d'exception,
on appelle des destructeurs des sous-objets déjà construits.
Qu'est-ce que les instances statiques aient à voir ici.
-- et obtenir une NullPointerException est justement agréable,
Par rapport à quoi ?
La norme laisse la liberté aux implémentations de faire quelque
chose d'intelligent. Mais effectivement, peu le font (VC++, je
crois, mais je crois que c'est le seul).
comprendre qu'ici la réalisation de l'appel (pas sa logique telle que
définie par la norme) est différente *parce que l'on est dans un
constructeur*; nous rerépétez que ce serait tjrs pareil se heurte à
l'expérience et n'apprend rien au final.
comprendre qu'ici la réalisation de l'appel (pas sa logique telle que
définie par la norme) est différente *parce que l'on est dans un
constructeur*; nous rerépétez que ce serait tjrs pareil se heurte à
l'expérience et n'apprend rien au final.
comprendre qu'ici la réalisation de l'appel (pas sa logique telle que
définie par la norme) est différente *parce que l'on est dans un
constructeur*; nous rerépétez que ce serait tjrs pareil se heurte à
l'expérience et n'apprend rien au final.
Je n'ai jamais entendu James dire que c'était pareil. Juste que c'était
quand même un appel virtuel, mais qu'il fallait faire attention que le
type considéré était le type courant, et non le type final.
Ce qui a le mérite d'être vrai, alors que dire que dans un constructeur,
on considère le type statique, que le mécanisme de virtualité est
supprimé, est faux.
Les exemples déjà cités le prouvent. Je trouve pour ma part l'exemple
plus parlant écrit ainsi :
Je n'ai jamais entendu James dire que c'était pareil. Juste que c'était
quand même un appel virtuel, mais qu'il fallait faire attention que le
type considéré était le type courant, et non le type final.
Ce qui a le mérite d'être vrai, alors que dire que dans un constructeur,
on considère le type statique, que le mécanisme de virtualité est
supprimé, est faux.
Les exemples déjà cités le prouvent. Je trouve pour ma part l'exemple
plus parlant écrit ainsi :
Je n'ai jamais entendu James dire que c'était pareil. Juste que c'était
quand même un appel virtuel, mais qu'il fallait faire attention que le
type considéré était le type courant, et non le type final.
Ce qui a le mérite d'être vrai, alors que dire que dans un constructeur,
on considère le type statique, que le mécanisme de virtualité est
supprimé, est faux.
Les exemples déjà cités le prouvent. Je trouve pour ma part l'exemple
plus parlant écrit ainsi :
kanze wrote on 12/07/2006 09:19:
comprendre qu'ici la réalisation de l'appel (pas sa logique
telle que définie par la norme) est différente *parce que l'on
est dans un constructeur*; nous rerépétez que ce serait tjrs
pareil se heurte à l'expérience et n'apprend rien au final.
Le problème, d'après mon expérience (concrète), c'est
plutôt avec la façon que fait Java, où on se trouve dans
une fonction membre sur un objet dont le constructeur n'a
pas encore été appelé. C'est une source d'erreurs
importante.
si tu écris des trus comme:
class Machin {
Truc truc;
Machin(){
maMethodeQuiTrucMuche();
truc = new Truc();
}
void maMethodeQuiTrucMuche(){
truc.muche();
}
};
t'as en effet un problème, mais pas de langage, de codage
Je vois que tes connaissances de Java sont aussi faible que
celles du C++. Essaie de dériver d'une classe de Swing, par
exemple, et de supplanter certaines fonctions virtuelles. Tu
te rétrouves vite dans la fonction sans que le constructeur
a été appelé, et avec toutes les variables membre à null.
- je supplante tous les jours de nombreuses méthodes des
classes de javax.swing (ou d'autres packages) sans péter des
NullPointerException par erreur de codage,
- que /je/ sois "faible" parce que /tu/ génères de telles
erreurs est risible et pitoyable
- tes attaques ad homimem et répétitives sont hors chartre et
sans aucun intérêt, un jour peut être te lasseras-tu, pour ma
part et depuis le primaire j'ai toujours laissé autrui gagner
quand il s'agit de jouer au plus con.
d'exception, on laisse tout comme il était, et on espère
pour le mieux. C'est quasiment impossible d'écrire un
programme « correct » dans de tels cas, mais qu'importe.
autre lacune Java ? je te rapelle qu'il y a un GC pour
récupérer "tout" et que tu as le droit de supplanter
finalize() si besoin.
En C++, en cas d'exception, on appelle des destructeurs des
sous-objets déjà construits.
a) donc j'avais faux d'envisager un delete dans le
constructeur ... c'est pas delete, c'est
"delete-dit-par-James" ...
b) "on" appele même un delete sur les objects-membre alloués,
voui, voui et on s'emmerde encore à écrire proprement des
destructeurs ...
faudrait arréter les amalgames, tu crois pas ? un handler
d'expection doit être codé correctement (re)"point".
Qu'est-ce que les instances statiques aient à voir ici.
cela t'éviterait une NullPtrExc. quand tu oublies d'appeler un
cst.
-- et obtenir une NullPointerException est justement agréable,
Par rapport à quoi ?
relis ton post (un cran au-dessus)
kanze wrote on 12/07/2006 09:19:
comprendre qu'ici la réalisation de l'appel (pas sa logique
telle que définie par la norme) est différente *parce que l'on
est dans un constructeur*; nous rerépétez que ce serait tjrs
pareil se heurte à l'expérience et n'apprend rien au final.
Le problème, d'après mon expérience (concrète), c'est
plutôt avec la façon que fait Java, où on se trouve dans
une fonction membre sur un objet dont le constructeur n'a
pas encore été appelé. C'est une source d'erreurs
importante.
si tu écris des trus comme:
class Machin {
Truc truc;
Machin(){
maMethodeQuiTrucMuche();
truc = new Truc();
}
void maMethodeQuiTrucMuche(){
truc.muche();
}
};
t'as en effet un problème, mais pas de langage, de codage
Je vois que tes connaissances de Java sont aussi faible que
celles du C++. Essaie de dériver d'une classe de Swing, par
exemple, et de supplanter certaines fonctions virtuelles. Tu
te rétrouves vite dans la fonction sans que le constructeur
a été appelé, et avec toutes les variables membre à null.
- je supplante tous les jours de nombreuses méthodes des
classes de javax.swing (ou d'autres packages) sans péter des
NullPointerException par erreur de codage,
- que /je/ sois "faible" parce que /tu/ génères de telles
erreurs est risible et pitoyable
- tes attaques ad homimem et répétitives sont hors chartre et
sans aucun intérêt, un jour peut être te lasseras-tu, pour ma
part et depuis le primaire j'ai toujours laissé autrui gagner
quand il s'agit de jouer au plus con.
d'exception, on laisse tout comme il était, et on espère
pour le mieux. C'est quasiment impossible d'écrire un
programme « correct » dans de tels cas, mais qu'importe.
autre lacune Java ? je te rapelle qu'il y a un GC pour
récupérer "tout" et que tu as le droit de supplanter
finalize() si besoin.
En C++, en cas d'exception, on appelle des destructeurs des
sous-objets déjà construits.
a) donc j'avais faux d'envisager un delete dans le
constructeur ... c'est pas delete, c'est
"delete-dit-par-James" ...
b) "on" appele même un delete sur les objects-membre alloués,
voui, voui et on s'emmerde encore à écrire proprement des
destructeurs ...
faudrait arréter les amalgames, tu crois pas ? un handler
d'expection doit être codé correctement (re)"point".
Qu'est-ce que les instances statiques aient à voir ici.
cela t'éviterait une NullPtrExc. quand tu oublies d'appeler un
cst.
-- et obtenir une NullPointerException est justement agréable,
Par rapport à quoi ?
relis ton post (un cran au-dessus)
kanze wrote on 12/07/2006 09:19:
comprendre qu'ici la réalisation de l'appel (pas sa logique
telle que définie par la norme) est différente *parce que l'on
est dans un constructeur*; nous rerépétez que ce serait tjrs
pareil se heurte à l'expérience et n'apprend rien au final.
Le problème, d'après mon expérience (concrète), c'est
plutôt avec la façon que fait Java, où on se trouve dans
une fonction membre sur un objet dont le constructeur n'a
pas encore été appelé. C'est une source d'erreurs
importante.
si tu écris des trus comme:
class Machin {
Truc truc;
Machin(){
maMethodeQuiTrucMuche();
truc = new Truc();
}
void maMethodeQuiTrucMuche(){
truc.muche();
}
};
t'as en effet un problème, mais pas de langage, de codage
Je vois que tes connaissances de Java sont aussi faible que
celles du C++. Essaie de dériver d'une classe de Swing, par
exemple, et de supplanter certaines fonctions virtuelles. Tu
te rétrouves vite dans la fonction sans que le constructeur
a été appelé, et avec toutes les variables membre à null.
- je supplante tous les jours de nombreuses méthodes des
classes de javax.swing (ou d'autres packages) sans péter des
NullPointerException par erreur de codage,
- que /je/ sois "faible" parce que /tu/ génères de telles
erreurs est risible et pitoyable
- tes attaques ad homimem et répétitives sont hors chartre et
sans aucun intérêt, un jour peut être te lasseras-tu, pour ma
part et depuis le primaire j'ai toujours laissé autrui gagner
quand il s'agit de jouer au plus con.
d'exception, on laisse tout comme il était, et on espère
pour le mieux. C'est quasiment impossible d'écrire un
programme « correct » dans de tels cas, mais qu'importe.
autre lacune Java ? je te rapelle qu'il y a un GC pour
récupérer "tout" et que tu as le droit de supplanter
finalize() si besoin.
En C++, en cas d'exception, on appelle des destructeurs des
sous-objets déjà construits.
a) donc j'avais faux d'envisager un delete dans le
constructeur ... c'est pas delete, c'est
"delete-dit-par-James" ...
b) "on" appele même un delete sur les objects-membre alloués,
voui, voui et on s'emmerde encore à écrire proprement des
destructeurs ...
faudrait arréter les amalgames, tu crois pas ? un handler
d'expection doit être codé correctement (re)"point".
Qu'est-ce que les instances statiques aient à voir ici.
cela t'éviterait une NullPtrExc. quand tu oublies d'appeler un
cst.
-- et obtenir une NullPointerException est justement agréable,
Par rapport à quoi ?
relis ton post (un cran au-dessus)
Loïc Joly wrote on 13/07/2006 00:16:Je n'ai jamais entendu James dire que c'était pareil. Juste
que c'était quand même un appel virtuel, mais qu'il fallait
faire attention que le
..type considéré était le type courant, et non le type final.
cette expression est pédagogiquement très compréhensible, je
ne crois pas l'avoir lu ainsi.
Loïc Joly wrote on 13/07/2006 00:16:
Je n'ai jamais entendu James dire que c'était pareil. Juste
que c'était quand même un appel virtuel, mais qu'il fallait
faire attention que le
..
type considéré était le type courant, et non le type final.
cette expression est pédagogiquement très compréhensible, je
ne crois pas l'avoir lu ainsi.
Loïc Joly wrote on 13/07/2006 00:16:Je n'ai jamais entendu James dire que c'était pareil. Juste
que c'était quand même un appel virtuel, mais qu'il fallait
faire attention que le
..type considéré était le type courant, et non le type final.
cette expression est pédagogiquement très compréhensible, je
ne crois pas l'avoir lu ainsi.
Vu autrement, lorsqu'un A est construit, il l'est avec une
vtable de A. Il n'a donc pas accès à la vtable de AA qui ne
sera « construite » que lors de la construction du AA (et donc, une f ois le A déjà
construit).
Ce qu'il ne faut pas oublier, en revanche, c'est que pendant
l'exécution d'un constructeur (et d'un destructeur), le type
dynamique est celui du constructeur (ou destructeur), et non
celui qu'il sera par la suite
En somme, rien de spécial : un comportement tout à fait normal,
celui auquel on s'attend pour peu qu'on y réflechit un peu, et
le seul possible qui est un peu cohérent. (On ne va quand même
pas choisir la fonction selon un état future incertain, non ?)
C'est une question de sécurité. Pour qu'un objet ait un type
dynamique T, il faut qu'on soit au moins entré dans le constructeur de T.
En C++, en cas d'exception, on appelle des destructeurs des sous-objets d éjà
construits.
C'est à dire qu'en remontant dans la pile des appels, tous les objets
Oui. À moins, par exemple, de passer par une factory amie qui
appellerait maMethode() juste après la construction.
Euh... si je comprends bien l'idée (je suis encore un peu noeud en
Il y a des astuces avec des paramètres, aussi. Si, à la place de
lui passer un B, on lui passe un InitAAvecB (lié à une référence
à const), classe connue d'A (et dont A est ami) qui a un
Argh... désolé Kanze, il va falloir etre plus explicite sur ce coup
Sinon, il n'est pas rare chez moi d'utiliser le modèle de
stratégie, et de mettre le comportement dynamique dans une
classe de délégation. Qu'on construire complètement, évidemment,
avant d'entrer dans le corps du constructeur d'A, ou au moins
avant d'appeler la fonction en question.
woush... pas plus... Un exemple rapide ? si tu as le temps...
Vu autrement, lorsqu'un A est construit, il l'est avec une
vtable de A. Il n'a donc pas accès à la vtable de AA qui ne
sera « construite » que lors de la construction du AA (et donc, une f ois le A déjà
construit).
Ce qu'il ne faut pas oublier, en revanche, c'est que pendant
l'exécution d'un constructeur (et d'un destructeur), le type
dynamique est celui du constructeur (ou destructeur), et non
celui qu'il sera par la suite
En somme, rien de spécial : un comportement tout à fait normal,
celui auquel on s'attend pour peu qu'on y réflechit un peu, et
le seul possible qui est un peu cohérent. (On ne va quand même
pas choisir la fonction selon un état future incertain, non ?)
C'est une question de sécurité. Pour qu'un objet ait un type
dynamique T, il faut qu'on soit au moins entré dans le constructeur de T.
En C++, en cas d'exception, on appelle des destructeurs des sous-objets d éjà
construits.
C'est à dire qu'en remontant dans la pile des appels, tous les objets
Oui. À moins, par exemple, de passer par une factory amie qui
appellerait maMethode() juste après la construction.
Euh... si je comprends bien l'idée (je suis encore un peu noeud en
Il y a des astuces avec des paramètres, aussi. Si, à la place de
lui passer un B, on lui passe un InitAAvecB (lié à une référence
à const), classe connue d'A (et dont A est ami) qui a un
Argh... désolé Kanze, il va falloir etre plus explicite sur ce coup
Sinon, il n'est pas rare chez moi d'utiliser le modèle de
stratégie, et de mettre le comportement dynamique dans une
classe de délégation. Qu'on construire complètement, évidemment,
avant d'entrer dans le corps du constructeur d'A, ou au moins
avant d'appeler la fonction en question.
woush... pas plus... Un exemple rapide ? si tu as le temps...
Vu autrement, lorsqu'un A est construit, il l'est avec une
vtable de A. Il n'a donc pas accès à la vtable de AA qui ne
sera « construite » que lors de la construction du AA (et donc, une f ois le A déjà
construit).
Ce qu'il ne faut pas oublier, en revanche, c'est que pendant
l'exécution d'un constructeur (et d'un destructeur), le type
dynamique est celui du constructeur (ou destructeur), et non
celui qu'il sera par la suite
En somme, rien de spécial : un comportement tout à fait normal,
celui auquel on s'attend pour peu qu'on y réflechit un peu, et
le seul possible qui est un peu cohérent. (On ne va quand même
pas choisir la fonction selon un état future incertain, non ?)
C'est une question de sécurité. Pour qu'un objet ait un type
dynamique T, il faut qu'on soit au moins entré dans le constructeur de T.
En C++, en cas d'exception, on appelle des destructeurs des sous-objets d éjà
construits.
C'est à dire qu'en remontant dans la pile des appels, tous les objets
Oui. À moins, par exemple, de passer par une factory amie qui
appellerait maMethode() juste après la construction.
Euh... si je comprends bien l'idée (je suis encore un peu noeud en
Il y a des astuces avec des paramètres, aussi. Si, à la place de
lui passer un B, on lui passe un InitAAvecB (lié à une référence
à const), classe connue d'A (et dont A est ami) qui a un
Argh... désolé Kanze, il va falloir etre plus explicite sur ce coup
Sinon, il n'est pas rare chez moi d'utiliser le modèle de
stratégie, et de mettre le comportement dynamique dans une
classe de délégation. Qu'on construire complètement, évidemment,
avant d'entrer dans le corps du constructeur d'A, ou au moins
avant d'appeler la fonction en question.
woush... pas plus... Un exemple rapide ? si tu as le temps...
c'est parti pour le dépouillage :
Que se passe t'il ? Logiquement je dirais :
[...]
execute AAAinit() et c'est fini. J'ai bon ?
En hors sujet maintenant :
+KanzeEn C++, en cas d'exception, on appelle des destructeurs des sous-objets déjà
construits.
C'est à dire qu'en remontant dans la pile des appels, tous les objets
"rencontrés" avant le "catch" sont détruits ?
Oui. À moins, par exemple, de passer par une factory amie qui
appellerait maMethode() juste après la construction.
Euh... si je comprends bien l'idée (je suis encore un peu noeud en
design) :
- mes classes A et AA auraient un constructeur en privé.
- J'aurai une classe F (Factory) amie de A qui construirait un A* sur
demande
F.giveMeA(A* a, kind), avec kind par exemple un enum {AA,AB,AC} pour
représenter les classes dérivées de A
- avec
giveMeA(A* a,kind){
switch (kind) {
case AA : a = new AA()
...
}
a->maFonction();
}
c'est parti pour le dépouillage :
Que se passe t'il ? Logiquement je dirais :
[...]
execute AAAinit() et c'est fini. J'ai bon ?
En hors sujet maintenant :
+Kanze
En C++, en cas d'exception, on appelle des destructeurs des sous-objets déjà
construits.
C'est à dire qu'en remontant dans la pile des appels, tous les objets
"rencontrés" avant le "catch" sont détruits ?
Oui. À moins, par exemple, de passer par une factory amie qui
appellerait maMethode() juste après la construction.
Euh... si je comprends bien l'idée (je suis encore un peu noeud en
design) :
- mes classes A et AA auraient un constructeur en privé.
- J'aurai une classe F (Factory) amie de A qui construirait un A* sur
demande
F.giveMeA(A* a, kind), avec kind par exemple un enum {AA,AB,AC} pour
représenter les classes dérivées de A
- avec
giveMeA(A* a,kind){
switch (kind) {
case AA : a = new AA()
...
}
a->maFonction();
}
c'est parti pour le dépouillage :
Que se passe t'il ? Logiquement je dirais :
[...]
execute AAAinit() et c'est fini. J'ai bon ?
En hors sujet maintenant :
+KanzeEn C++, en cas d'exception, on appelle des destructeurs des sous-objets déjà
construits.
C'est à dire qu'en remontant dans la pile des appels, tous les objets
"rencontrés" avant le "catch" sont détruits ?
Oui. À moins, par exemple, de passer par une factory amie qui
appellerait maMethode() juste après la construction.
Euh... si je comprends bien l'idée (je suis encore un peu noeud en
design) :
- mes classes A et AA auraient un constructeur en privé.
- J'aurai une classe F (Factory) amie de A qui construirait un A* sur
demande
F.giveMeA(A* a, kind), avec kind par exemple un enum {AA,AB,AC} pour
représenter les classes dérivées de A
- avec
giveMeA(A* a,kind){
switch (kind) {
case AA : a = new AA()
...
}
a->maFonction();
}
Au passage, j'ai trouvé un extrait (je dirais bien « l'extrait », m ais
OK, merci.
Sachant que la gestion des vtables peut être compliqué par l'hérita ge
multiple (on hérite alors de deux vtables et « écraser » une vtab le
devient quelque chose de moins évident).
Certes... Tu as des actions chez Aspro ? ;)
Au passage, j'ai trouvé un extrait (je dirais bien « l'extrait », m ais
OK, merci.
Sachant que la gestion des vtables peut être compliqué par l'hérita ge
multiple (on hérite alors de deux vtables et « écraser » une vtab le
devient quelque chose de moins évident).
Certes... Tu as des actions chez Aspro ? ;)
Au passage, j'ai trouvé un extrait (je dirais bien « l'extrait », m ais
OK, merci.
Sachant que la gestion des vtables peut être compliqué par l'hérita ge
multiple (on hérite alors de deux vtables et « écraser » une vtab le
devient quelque chose de moins évident).
Certes... Tu as des actions chez Aspro ? ;)
Bon... Je reviens à mon post après renseigné sur les quelques
infos connexes que vos premières réponses m'on fait penser
indispensable. Je ne connaissais pas le mécanisme des vtables
par exemple... Bon, c'est parti pour le dépouillage :
1. si j'ai bien compris, la vtable n'est pas dans la norme
C++, c'est juste un mécanisme proposé pour la gestion des
fonctions virtuelles, et en l'occurrence, le mécanisme
effectivement implanté par (tous?!) les compilos.
+ArnaudVu autrement, lorsqu'un A est construit, il l'est avec une
vtable de A. Il n'a donc pas accès à la vtable de AA qui ne
sera « construite » que lors de la construction du AA (et
donc, une fois le A déjà construit).
Si je comprends bien :
lors de l'execution du constructeur
AA(...):A(...){...}
on commence par construire A, et en particulier on initialise
le pointeur sur vtable de l'objet avec l'adresse de vtablle de
la classe A. Puis, lorsqu'on sort de là, on finit
l'initialisation de l'objet en écrasant cette valeur par celle
de l'adresse de vtable de la classe AA. Enfin on termine la
construction en rentrant dans le bloc {...}. Au passage, dans
une arborescence à plusieurs étages, la vtable est donc
remplie et ecrasée autant de fois qu'on a d'ancètre.
+KanzeCe qu'il ne faut pas oublier, en revanche, c'est que pendant
l'exécution d'un constructeur (et d'un destructeur), le type
dynamique est celui du constructeur (ou destructeur), et non
celui qu'il sera par la suite
C'est dans la norme, ou bien c'est un corrollaire de
l'implémentation sus-décrite ?
+KanzeEn somme, rien de spécial : un comportement tout à fait normal,
celui auquel on s'attend pour peu qu'on y réflechit un peu, et
le seul possible qui est un peu cohérent. (On ne va quand même
pas choisir la fonction selon un état future incertain, non ?)
chais pas moi...
<mode zadig>On pourrait imaginer que lors de la construction d'un AA on
commence cash par remplir la vtable, puis lors de l'appel des
constructeurs des classes de base il y aurait un mécanisme qui
remarquerait que la vtable est déjà remplie et on y toucherait
pas</mode>
Mais si je comprends bien une assertion qui suit dans le fil de
discussion, j'ai une réponse qui laisse entendre que c'est ce que fait
java et que ça peut etre une mauvaise idée :
+KanzeC'est une question de sécurité. Pour qu'un objet ait un type
dynamique T, il faut qu'on soit au moins entré dans le
constructeur de T.
L'idée donc c'est que si je n'ai pas encore initialisé tout
mon AA, le fait d'utiliser des fonctions de AA pourrait
m'emmener dans le mur par exemple en jouant avec des pointeurs
non initialisés... Du coup, on préfère me filer des pointeurs
vers des fonctions qui ne jouent qu'avec des champs déjà
initialisés. Mouais, c'est vrai que tout ça se vaut :)
Du coup je me pose quand meme la question de l'ordre dans
lequel les fonctions sont appelée. Admettons que j'ai une
arborescence A base de AA base de AAA avec du virtuel dans
tout ça et des constructeurs du genre :
AAA():AA(){AAAinit();}
AA():A(){AAinit();}
A(){Ainit()}
lorsque j'écris
AAA aaa;
Que se passe t'il ? Logiquement je dirais :
On commence par appeller AA(), qui lui appelle A(), qui
initialise par défaut ses champs éventuels, puis initialise le
pointeur de vtable de mon objet aaa avec l'adresse de celle de
A. Puis on entre dans le corps du constructeur de A d'où appel
à Ainit. Hop, on sort, on écrase l'adresse de vtable avec
celle de AA, on joue avec AAinit(), on écrase l'adresse vtable
avec celle de AAA, on execute AAAinit() et c'est fini. J'ai
bon ?
En hors sujet maintenant :
+KanzeEn C++, en cas d'exception, on appelle des destructeurs des
sous-objets déjà construits.
C'est à dire qu'en remontant dans la pile des appels, tous les
objets "rencontrés" avant le "catch" sont détruits ?
Et pour revenir à mon problème concret :
+ ArnaudOui. À moins, par exemple, de passer par une factory amie qui
appellerait maMethode() juste après la construction.
Euh... si je comprends bien l'idée (je suis encore un peu noeud en
design) :
- mes classes A et AA auraient un constructeur en privé.
- J'aurai une classe F (Factory) amie de A qui construirait un A* sur
demande
F.giveMeA(A* a, kind), avec kind par exemple un enum {AA,AB,AC} pour
représenter les classes dérivées de A
- avec
giveMeA(A* a,kind){
switch (kind) {
case AA : a = new AA()
...
}
a->maFonction();
}
+ KanzeIl y a des astuces avec des paramètres, aussi. Si, à la
place de lui passer un B, on lui passe un InitAAvecB (lié à
une référence à const), classe connue d'A (et dont A est
ami) qui a un
Argh... désolé Kanze, il va falloir etre plus explicite sur ce
coup :D
+ KanzeSinon, il n'est pas rare chez moi d'utiliser le modèle de
stratégie, et de mettre le comportement dynamique dans une
classe de délégation. Qu'on construire complètement,
évidemment, avant d'entrer dans le corps du constructeur
d'A, ou au moins avant d'appeler la fonction en question.
woush... pas plus... Un exemple rapide ? si tu as le temps...
Bon... Je reviens à mon post après renseigné sur les quelques
infos connexes que vos premières réponses m'on fait penser
indispensable. Je ne connaissais pas le mécanisme des vtables
par exemple... Bon, c'est parti pour le dépouillage :
1. si j'ai bien compris, la vtable n'est pas dans la norme
C++, c'est juste un mécanisme proposé pour la gestion des
fonctions virtuelles, et en l'occurrence, le mécanisme
effectivement implanté par (tous?!) les compilos.
+Arnaud
Vu autrement, lorsqu'un A est construit, il l'est avec une
vtable de A. Il n'a donc pas accès à la vtable de AA qui ne
sera « construite » que lors de la construction du AA (et
donc, une fois le A déjà construit).
Si je comprends bien :
lors de l'execution du constructeur
AA(...):A(...){...}
on commence par construire A, et en particulier on initialise
le pointeur sur vtable de l'objet avec l'adresse de vtablle de
la classe A. Puis, lorsqu'on sort de là, on finit
l'initialisation de l'objet en écrasant cette valeur par celle
de l'adresse de vtable de la classe AA. Enfin on termine la
construction en rentrant dans le bloc {...}. Au passage, dans
une arborescence à plusieurs étages, la vtable est donc
remplie et ecrasée autant de fois qu'on a d'ancètre.
+Kanze
Ce qu'il ne faut pas oublier, en revanche, c'est que pendant
l'exécution d'un constructeur (et d'un destructeur), le type
dynamique est celui du constructeur (ou destructeur), et non
celui qu'il sera par la suite
C'est dans la norme, ou bien c'est un corrollaire de
l'implémentation sus-décrite ?
+Kanze
En somme, rien de spécial : un comportement tout à fait normal,
celui auquel on s'attend pour peu qu'on y réflechit un peu, et
le seul possible qui est un peu cohérent. (On ne va quand même
pas choisir la fonction selon un état future incertain, non ?)
chais pas moi...
<mode zadig>On pourrait imaginer que lors de la construction d'un AA on
commence cash par remplir la vtable, puis lors de l'appel des
constructeurs des classes de base il y aurait un mécanisme qui
remarquerait que la vtable est déjà remplie et on y toucherait
pas</mode>
Mais si je comprends bien une assertion qui suit dans le fil de
discussion, j'ai une réponse qui laisse entendre que c'est ce que fait
java et que ça peut etre une mauvaise idée :
+Kanze
C'est une question de sécurité. Pour qu'un objet ait un type
dynamique T, il faut qu'on soit au moins entré dans le
constructeur de T.
L'idée donc c'est que si je n'ai pas encore initialisé tout
mon AA, le fait d'utiliser des fonctions de AA pourrait
m'emmener dans le mur par exemple en jouant avec des pointeurs
non initialisés... Du coup, on préfère me filer des pointeurs
vers des fonctions qui ne jouent qu'avec des champs déjà
initialisés. Mouais, c'est vrai que tout ça se vaut :)
Du coup je me pose quand meme la question de l'ordre dans
lequel les fonctions sont appelée. Admettons que j'ai une
arborescence A base de AA base de AAA avec du virtuel dans
tout ça et des constructeurs du genre :
AAA():AA(){AAAinit();}
AA():A(){AAinit();}
A(){Ainit()}
lorsque j'écris
AAA aaa;
Que se passe t'il ? Logiquement je dirais :
On commence par appeller AA(), qui lui appelle A(), qui
initialise par défaut ses champs éventuels, puis initialise le
pointeur de vtable de mon objet aaa avec l'adresse de celle de
A. Puis on entre dans le corps du constructeur de A d'où appel
à Ainit. Hop, on sort, on écrase l'adresse de vtable avec
celle de AA, on joue avec AAinit(), on écrase l'adresse vtable
avec celle de AAA, on execute AAAinit() et c'est fini. J'ai
bon ?
En hors sujet maintenant :
+Kanze
En C++, en cas d'exception, on appelle des destructeurs des
sous-objets déjà construits.
C'est à dire qu'en remontant dans la pile des appels, tous les
objets "rencontrés" avant le "catch" sont détruits ?
Et pour revenir à mon problème concret :
+ Arnaud
Oui. À moins, par exemple, de passer par une factory amie qui
appellerait maMethode() juste après la construction.
Euh... si je comprends bien l'idée (je suis encore un peu noeud en
design) :
- mes classes A et AA auraient un constructeur en privé.
- J'aurai une classe F (Factory) amie de A qui construirait un A* sur
demande
F.giveMeA(A* a, kind), avec kind par exemple un enum {AA,AB,AC} pour
représenter les classes dérivées de A
- avec
giveMeA(A* a,kind){
switch (kind) {
case AA : a = new AA()
...
}
a->maFonction();
}
+ Kanze
Il y a des astuces avec des paramètres, aussi. Si, à la
place de lui passer un B, on lui passe un InitAAvecB (lié à
une référence à const), classe connue d'A (et dont A est
ami) qui a un
Argh... désolé Kanze, il va falloir etre plus explicite sur ce
coup :D
+ Kanze
Sinon, il n'est pas rare chez moi d'utiliser le modèle de
stratégie, et de mettre le comportement dynamique dans une
classe de délégation. Qu'on construire complètement,
évidemment, avant d'entrer dans le corps du constructeur
d'A, ou au moins avant d'appeler la fonction en question.
woush... pas plus... Un exemple rapide ? si tu as le temps...
Bon... Je reviens à mon post après renseigné sur les quelques
infos connexes que vos premières réponses m'on fait penser
indispensable. Je ne connaissais pas le mécanisme des vtables
par exemple... Bon, c'est parti pour le dépouillage :
1. si j'ai bien compris, la vtable n'est pas dans la norme
C++, c'est juste un mécanisme proposé pour la gestion des
fonctions virtuelles, et en l'occurrence, le mécanisme
effectivement implanté par (tous?!) les compilos.
+ArnaudVu autrement, lorsqu'un A est construit, il l'est avec une
vtable de A. Il n'a donc pas accès à la vtable de AA qui ne
sera « construite » que lors de la construction du AA (et
donc, une fois le A déjà construit).
Si je comprends bien :
lors de l'execution du constructeur
AA(...):A(...){...}
on commence par construire A, et en particulier on initialise
le pointeur sur vtable de l'objet avec l'adresse de vtablle de
la classe A. Puis, lorsqu'on sort de là, on finit
l'initialisation de l'objet en écrasant cette valeur par celle
de l'adresse de vtable de la classe AA. Enfin on termine la
construction en rentrant dans le bloc {...}. Au passage, dans
une arborescence à plusieurs étages, la vtable est donc
remplie et ecrasée autant de fois qu'on a d'ancètre.
+KanzeCe qu'il ne faut pas oublier, en revanche, c'est que pendant
l'exécution d'un constructeur (et d'un destructeur), le type
dynamique est celui du constructeur (ou destructeur), et non
celui qu'il sera par la suite
C'est dans la norme, ou bien c'est un corrollaire de
l'implémentation sus-décrite ?
+KanzeEn somme, rien de spécial : un comportement tout à fait normal,
celui auquel on s'attend pour peu qu'on y réflechit un peu, et
le seul possible qui est un peu cohérent. (On ne va quand même
pas choisir la fonction selon un état future incertain, non ?)
chais pas moi...
<mode zadig>On pourrait imaginer que lors de la construction d'un AA on
commence cash par remplir la vtable, puis lors de l'appel des
constructeurs des classes de base il y aurait un mécanisme qui
remarquerait que la vtable est déjà remplie et on y toucherait
pas</mode>
Mais si je comprends bien une assertion qui suit dans le fil de
discussion, j'ai une réponse qui laisse entendre que c'est ce que fait
java et que ça peut etre une mauvaise idée :
+KanzeC'est une question de sécurité. Pour qu'un objet ait un type
dynamique T, il faut qu'on soit au moins entré dans le
constructeur de T.
L'idée donc c'est que si je n'ai pas encore initialisé tout
mon AA, le fait d'utiliser des fonctions de AA pourrait
m'emmener dans le mur par exemple en jouant avec des pointeurs
non initialisés... Du coup, on préfère me filer des pointeurs
vers des fonctions qui ne jouent qu'avec des champs déjà
initialisés. Mouais, c'est vrai que tout ça se vaut :)
Du coup je me pose quand meme la question de l'ordre dans
lequel les fonctions sont appelée. Admettons que j'ai une
arborescence A base de AA base de AAA avec du virtuel dans
tout ça et des constructeurs du genre :
AAA():AA(){AAAinit();}
AA():A(){AAinit();}
A(){Ainit()}
lorsque j'écris
AAA aaa;
Que se passe t'il ? Logiquement je dirais :
On commence par appeller AA(), qui lui appelle A(), qui
initialise par défaut ses champs éventuels, puis initialise le
pointeur de vtable de mon objet aaa avec l'adresse de celle de
A. Puis on entre dans le corps du constructeur de A d'où appel
à Ainit. Hop, on sort, on écrase l'adresse de vtable avec
celle de AA, on joue avec AAinit(), on écrase l'adresse vtable
avec celle de AAA, on execute AAAinit() et c'est fini. J'ai
bon ?
En hors sujet maintenant :
+KanzeEn C++, en cas d'exception, on appelle des destructeurs des
sous-objets déjà construits.
C'est à dire qu'en remontant dans la pile des appels, tous les
objets "rencontrés" avant le "catch" sont détruits ?
Et pour revenir à mon problème concret :
+ ArnaudOui. À moins, par exemple, de passer par une factory amie qui
appellerait maMethode() juste après la construction.
Euh... si je comprends bien l'idée (je suis encore un peu noeud en
design) :
- mes classes A et AA auraient un constructeur en privé.
- J'aurai une classe F (Factory) amie de A qui construirait un A* sur
demande
F.giveMeA(A* a, kind), avec kind par exemple un enum {AA,AB,AC} pour
représenter les classes dérivées de A
- avec
giveMeA(A* a,kind){
switch (kind) {
case AA : a = new AA()
...
}
a->maFonction();
}
+ KanzeIl y a des astuces avec des paramètres, aussi. Si, à la
place de lui passer un B, on lui passe un InitAAvecB (lié à
une référence à const), classe connue d'A (et dont A est
ami) qui a un
Argh... désolé Kanze, il va falloir etre plus explicite sur ce
coup :D
+ KanzeSinon, il n'est pas rare chez moi d'utiliser le modèle de
stratégie, et de mettre le comportement dynamique dans une
classe de délégation. Qu'on construire complètement,
évidemment, avant d'entrer dans le corps du constructeur
d'A, ou au moins avant d'appeler la fonction en question.
woush... pas plus... Un exemple rapide ? si tu as le temps...