Cette action est irreversible, confirmez la suppression du commentaire ?
Signaler le commentaire
Veuillez sélectionner un problème
Nudité
Violence
Harcèlement
Fraude
Vente illégale
Discours haineux
Terrorisme
Autre
Frédéric Lachasse
"Vincent Richard" wrote in message news:4024f0a5$0$28145$
Bonjour,
Soit le système de classes :
class A { }; class B : virtual public A { public: void f(); }; class C : virtual public A { public: void g(); }; class D : public B, public C { };
Sachant que tous les objets contenus dans le vecteur sont de même type dynamique (par exemple 'D'), est-il valide d'écrire :
std::vector <A*> v;
// ...
// Un "dynamic_cast" une fois pour toutes... const int offset = static_cast <int>(dynamic_cast <C*>(v[0]) - v[0]);
for (std::vector <A*>::iterator it = v.begin() ; it != v.end() ; ++it) { static_cast <C*>(*it + offset)->f(); }
Ceci afin d'éviter la multitude de "dynamic_cast" (lent) avec des vecteurs qui contiennent un grand nombre d'éléments (par exemple 1000).
A mon avis, les compilateurs vont se plaindre.
Ce que tu veux faire, c'est:
const int offset = reinterpret_cast<char*>(dynamic_cast<C*>(v[0])) - reinterpret_cast<char*>(v[0]);
for (std::vector<A*>::iterator it = v.begin(); it != v.end(); ++it) { reinterpret_cast<C*>(reinterpret_cast<char*>(*it) + offset)->f(); }
L'utilisation de reinterpret_cast<> montre que la méthode n'est pas complètement portable, mais cela marchera probablement sur toutes les platformes courantes.
Une méthode pour optimiser ponctuellement un dynamic_cast<>: ajouter une fonction virtuelle dans A qui retourne un C*. L'implémentation pour A retourne NULL et dans C retourne this.
-- Frédéric Lachasse - ECP86
"Vincent Richard" <chere-loque.MARRE-DE-LA-PUB@wanadoo.fr.invalid> wrote in
message news:4024f0a5$0$28145$626a14ce@news.free.fr...
Bonjour,
Soit le système de classes :
class A { };
class B : virtual public A { public: void f(); };
class C : virtual public A { public: void g(); };
class D : public B, public C { };
Sachant que tous les objets contenus dans le vecteur sont de même type
dynamique (par exemple 'D'), est-il valide d'écrire :
std::vector <A*> v;
// ...
// Un "dynamic_cast" une fois pour toutes...
const int offset = static_cast <int>(dynamic_cast <C*>(v[0]) - v[0]);
for (std::vector <A*>::iterator it = v.begin() ; it != v.end() ; ++it)
{
static_cast <C*>(*it + offset)->f();
}
Ceci afin d'éviter la multitude de "dynamic_cast" (lent) avec des vecteurs
qui contiennent un grand nombre d'éléments (par exemple 1000).
A mon avis, les compilateurs vont se plaindre.
Ce que tu veux faire, c'est:
const int offset = reinterpret_cast<char*>(dynamic_cast<C*>(v[0])) -
reinterpret_cast<char*>(v[0]);
for (std::vector<A*>::iterator it = v.begin(); it != v.end(); ++it)
{
reinterpret_cast<C*>(reinterpret_cast<char*>(*it) + offset)->f();
}
L'utilisation de reinterpret_cast<> montre que la méthode n'est pas
complètement portable, mais cela marchera probablement sur toutes les
platformes courantes.
Une méthode pour optimiser ponctuellement un dynamic_cast<>: ajouter une
fonction virtuelle dans A qui retourne un C*. L'implémentation pour A
retourne NULL et dans C retourne this.
"Vincent Richard" wrote in message news:4024f0a5$0$28145$
Bonjour,
Soit le système de classes :
class A { }; class B : virtual public A { public: void f(); }; class C : virtual public A { public: void g(); }; class D : public B, public C { };
Sachant que tous les objets contenus dans le vecteur sont de même type dynamique (par exemple 'D'), est-il valide d'écrire :
std::vector <A*> v;
// ...
// Un "dynamic_cast" une fois pour toutes... const int offset = static_cast <int>(dynamic_cast <C*>(v[0]) - v[0]);
for (std::vector <A*>::iterator it = v.begin() ; it != v.end() ; ++it) { static_cast <C*>(*it + offset)->f(); }
Ceci afin d'éviter la multitude de "dynamic_cast" (lent) avec des vecteurs qui contiennent un grand nombre d'éléments (par exemple 1000).
A mon avis, les compilateurs vont se plaindre.
Ce que tu veux faire, c'est:
const int offset = reinterpret_cast<char*>(dynamic_cast<C*>(v[0])) - reinterpret_cast<char*>(v[0]);
for (std::vector<A*>::iterator it = v.begin(); it != v.end(); ++it) { reinterpret_cast<C*>(reinterpret_cast<char*>(*it) + offset)->f(); }
L'utilisation de reinterpret_cast<> montre que la méthode n'est pas complètement portable, mais cela marchera probablement sur toutes les platformes courantes.
Une méthode pour optimiser ponctuellement un dynamic_cast<>: ajouter une fonction virtuelle dans A qui retourne un C*. L'implémentation pour A retourne NULL et dans C retourne this.
-- Frédéric Lachasse - ECP86
Vincent Richard
Une méthode pour optimiser ponctuellement un dynamic_cast<>: ajouter une fonction virtuelle dans A qui retourne un C*. L'implémentation pour A retourne NULL et dans C retourne this.
Le problème c'est que ma classe 'A' n'a pas (et ne doit pas avoir) "connaissance" des classes dérivées (il peut y en avoir plusieurs)...
Bon, de toutes façons, je pense que je vais laisser les "dynamic_cast", ça n'a pas l'air si long que ça de toutes façons (et du moment que ça fonctionne, c'est le principal).
Vincent
-- vmime, une bibliothèque C++ sous licence GPL pour parser et générer des messages au format MIME : http://www.sourceforge.net/projects/vmime/
Une méthode pour optimiser ponctuellement un dynamic_cast<>: ajouter une
fonction virtuelle dans A qui retourne un C*. L'implémentation pour A
retourne NULL et dans C retourne this.
Le problème c'est que ma classe 'A' n'a pas (et ne doit pas avoir)
"connaissance" des classes dérivées (il peut y en avoir plusieurs)...
Bon, de toutes façons, je pense que je vais laisser les "dynamic_cast",
ça n'a pas l'air si long que ça de toutes façons (et du moment que ça
fonctionne, c'est le principal).
Vincent
--
vmime, une bibliothèque C++ sous licence GPL pour parser et générer
des messages au format MIME : http://www.sourceforge.net/projects/vmime/
Une méthode pour optimiser ponctuellement un dynamic_cast<>: ajouter une fonction virtuelle dans A qui retourne un C*. L'implémentation pour A retourne NULL et dans C retourne this.
Le problème c'est que ma classe 'A' n'a pas (et ne doit pas avoir) "connaissance" des classes dérivées (il peut y en avoir plusieurs)...
Bon, de toutes façons, je pense que je vais laisser les "dynamic_cast", ça n'a pas l'air si long que ça de toutes façons (et du moment que ça fonctionne, c'est le principal).
Vincent
-- vmime, une bibliothèque C++ sous licence GPL pour parser et générer des messages au format MIME : http://www.sourceforge.net/projects/vmime/
Benoit Dejean
Le Sat, 07 Feb 2004 15:06:37 +0100, Vincent Richard a écrit :
Bonjour,
Soit le système de classes :
class A { }; class B : virtual public A { public: void f(); }; class C : virtual public A { public: void g(); }; class D : public B, public C { };
Sachant que tous les objets contenus dans le vecteur sont de même type dynamique (par exemple 'D'), est-il valide d'écrire :
qu'est-ce qui t'empêche d'exploiter cela, le fait que tous tes éléments soit de même type ?
std::vector <A*> v;
// ...
// Un "dynamic_cast" une fois pour toutes... const int offset = static_cast <int>(dynamic_cast <C*>(v[0]) - v[0]);
for (std::vector <A*>::iterator it = v.begin() ; it != v.end() ; ++it) { static_cast <C*>(*it + offset)->f(); }
Ceci afin d'éviter la multitude de "dynamic_cast" (lent) avec des vecteurs qui contiennent un grand nombre d'éléments (par exemple 1000).
comment ça lent ?
Le Sat, 07 Feb 2004 15:06:37 +0100, Vincent Richard a écrit :
Bonjour,
Soit le système de classes :
class A { };
class B : virtual public A { public: void f(); };
class C : virtual public A { public: void g(); };
class D : public B, public C { };
Sachant que tous les objets contenus dans le vecteur sont de même type
dynamique (par exemple 'D'), est-il valide d'écrire :
qu'est-ce qui t'empêche d'exploiter cela, le fait que tous tes éléments
soit de même type ?
std::vector <A*> v;
// ...
// Un "dynamic_cast" une fois pour toutes...
const int offset = static_cast <int>(dynamic_cast <C*>(v[0]) - v[0]);
for (std::vector <A*>::iterator it = v.begin() ; it != v.end() ; ++it)
{
static_cast <C*>(*it + offset)->f();
}
Ceci afin d'éviter la multitude de "dynamic_cast" (lent) avec des vecteurs
qui contiennent un grand nombre d'éléments (par exemple 1000).
Le Sat, 07 Feb 2004 15:06:37 +0100, Vincent Richard a écrit :
Bonjour,
Soit le système de classes :
class A { }; class B : virtual public A { public: void f(); }; class C : virtual public A { public: void g(); }; class D : public B, public C { };
Sachant que tous les objets contenus dans le vecteur sont de même type dynamique (par exemple 'D'), est-il valide d'écrire :
qu'est-ce qui t'empêche d'exploiter cela, le fait que tous tes éléments soit de même type ?
std::vector <A*> v;
// ...
// Un "dynamic_cast" une fois pour toutes... const int offset = static_cast <int>(dynamic_cast <C*>(v[0]) - v[0]);
for (std::vector <A*>::iterator it = v.begin() ; it != v.end() ; ++it) { static_cast <C*>(*it + offset)->f(); }
Ceci afin d'éviter la multitude de "dynamic_cast" (lent) avec des vecteurs qui contiennent un grand nombre d'éléments (par exemple 1000).
comment ça lent ?
Vincent Richard
class A { }; class B : virtual public A { public: void f(); }; class C : virtual public A { public: void g(); }; class D : public B, public C { };
Sachant que tous les objets contenus dans le vecteur sont de même type dynamique (par exemple 'D'), est-il valide d'écrire :
qu'est-ce qui t'empêche d'exploiter cela, le fait que tous tes éléments soit de même type ?
Justement, c'est bien ce que je veux faire ! Le vecteur contient des élements de type A* (non changeable), et je suis sûr que ce sont des objets de type C.
La classe A est celle qui est exposée à l'utilisateur (classe abstraite) pour qu'il puisse manipuler des objets concrets de manière transparente.
Ceci afin d'éviter la multitude de "dynamic_cast" (lent) avec des vecteurs qui contiennent un grand nombre d'éléments (par exemple 1000).
comment ça lent ?
Dans le cas de l'héritage multiple, un "dynamic_cast" est logiquement beaucoup plus coûteux qu'un simple "static_cast". Par contre, comme je l'ai dis dans ma précédente réponse, j'ai l'impression que ça ne vaut pas la peine de s'embêter pour si peu...
Vincent
-- vmime, une bibliothèque C++ sous licence GPL pour parser et générer des messages au format MIME : http://www.sourceforge.net/projects/vmime/
class A { };
class B : virtual public A { public: void f(); };
class C : virtual public A { public: void g(); };
class D : public B, public C { };
Sachant que tous les objets contenus dans le vecteur sont de même type
dynamique (par exemple 'D'), est-il valide d'écrire :
qu'est-ce qui t'empêche d'exploiter cela, le fait que tous tes éléments
soit de même type ?
Justement, c'est bien ce que je veux faire ! Le vecteur contient des
élements de type A* (non changeable), et je suis sûr que ce sont des
objets de type C.
La classe A est celle qui est exposée à l'utilisateur (classe abstraite)
pour qu'il puisse manipuler des objets concrets de manière transparente.
Ceci afin d'éviter la multitude de "dynamic_cast" (lent) avec des
vecteurs qui contiennent un grand nombre d'éléments (par exemple 1000).
comment ça lent ?
Dans le cas de l'héritage multiple, un "dynamic_cast" est logiquement
beaucoup plus coûteux qu'un simple "static_cast". Par contre, comme je
l'ai dis dans ma précédente réponse, j'ai l'impression que ça ne vaut
pas la peine de s'embêter pour si peu...
Vincent
--
vmime, une bibliothèque C++ sous licence GPL pour parser et générer
des messages au format MIME : http://www.sourceforge.net/projects/vmime/
class A { }; class B : virtual public A { public: void f(); }; class C : virtual public A { public: void g(); }; class D : public B, public C { };
Sachant que tous les objets contenus dans le vecteur sont de même type dynamique (par exemple 'D'), est-il valide d'écrire :
qu'est-ce qui t'empêche d'exploiter cela, le fait que tous tes éléments soit de même type ?
Justement, c'est bien ce que je veux faire ! Le vecteur contient des élements de type A* (non changeable), et je suis sûr que ce sont des objets de type C.
La classe A est celle qui est exposée à l'utilisateur (classe abstraite) pour qu'il puisse manipuler des objets concrets de manière transparente.
Ceci afin d'éviter la multitude de "dynamic_cast" (lent) avec des vecteurs qui contiennent un grand nombre d'éléments (par exemple 1000).
comment ça lent ?
Dans le cas de l'héritage multiple, un "dynamic_cast" est logiquement beaucoup plus coûteux qu'un simple "static_cast". Par contre, comme je l'ai dis dans ma précédente réponse, j'ai l'impression que ça ne vaut pas la peine de s'embêter pour si peu...
Vincent
-- vmime, une bibliothèque C++ sous licence GPL pour parser et générer des messages au format MIME : http://www.sourceforge.net/projects/vmime/
Fabien LE LEZ
On Sat, 07 Feb 2004 21:37:33 +0100, Vincent Richard wrote:
logiquement beaucoup plus coûteux
Honnêtement, ce genre d'intuitions s'avère rarement. Pour savoir si une méthode est lente ou rapide, il faut l'implémenter et la chronométrer.
-- ;-)
On Sat, 07 Feb 2004 21:37:33 +0100, Vincent Richard
<chere-loque.MARRE-DE-LA-PUB@wanadoo.fr.invalid> wrote:
logiquement beaucoup plus coûteux
Honnêtement, ce genre d'intuitions s'avère rarement. Pour savoir si
une méthode est lente ou rapide, il faut l'implémenter et la
chronométrer.
On Sat, 07 Feb 2004 21:37:33 +0100, Vincent Richard wrote:
logiquement beaucoup plus coûteux
Honnêtement, ce genre d'intuitions s'avère rarement. Pour savoir si une méthode est lente ou rapide, il faut l'implémenter et la chronométrer.
-- ;-)
David Geldreich
Bonjour a tous,
Vincent Richard wrote:
Justement, c'est bien ce que je veux faire ! Le vecteur contient des élements de type A* (non changeable), et je suis sûr que ce sont des objets de type C.
si tu es sûr que tout est du C*, un static_cast<C*> suffit. Par contre, pour vérifier cette assertion, je rajouterais un
assert(dynamic_cast<C*>(*it) != NULL);
Ainsi, si un jour ce n'est plus vrai, tu pourras remettre en cause ton optimisation.
David.
Bonjour a tous,
Vincent Richard wrote:
Justement, c'est bien ce que je veux faire ! Le vecteur contient des
élements de type A* (non changeable), et je suis sûr que ce sont des
objets de type C.
si tu es sûr que tout est du C*, un static_cast<C*> suffit. Par contre,
pour vérifier cette assertion, je rajouterais un
assert(dynamic_cast<C*>(*it) != NULL);
Ainsi, si un jour ce n'est plus vrai, tu pourras remettre en cause ton
optimisation.
Justement, c'est bien ce que je veux faire ! Le vecteur contient des élements de type A* (non changeable), et je suis sûr que ce sont des objets de type C.
si tu es sûr que tout est du C*, un static_cast<C*> suffit. Par contre, pour vérifier cette assertion, je rajouterais un
assert(dynamic_cast<C*>(*it) != NULL);
Ainsi, si un jour ce n'est plus vrai, tu pourras remettre en cause ton optimisation.
David.
Vincent Richard
Justement, c'est bien ce que je veux faire ! Le vecteur contient des élements de type A* (non changeable), et je suis sûr que ce sont des objets de type C.
si tu es sûr que tout est du C*, un static_cast<C*> suffit.
Même dans le cas d'héritage multiple ? Il me semblait que ça n'était plus vrai...
Vincent
-- vmime, une bibliothèque C++ sous licence GPL pour parser et générer des messages au format MIME : http://www.sourceforge.net/projects/vmime/
Justement, c'est bien ce que je veux faire ! Le vecteur contient des
élements de type A* (non changeable), et je suis sûr que ce sont des
objets de type C.
si tu es sûr que tout est du C*, un static_cast<C*> suffit.
Même dans le cas d'héritage multiple ?
Il me semblait que ça n'était plus vrai...
Vincent
--
vmime, une bibliothèque C++ sous licence GPL pour parser et générer
des messages au format MIME : http://www.sourceforge.net/projects/vmime/
Justement, c'est bien ce que je veux faire ! Le vecteur contient des élements de type A* (non changeable), et je suis sûr que ce sont des objets de type C.
si tu es sûr que tout est du C*, un static_cast<C*> suffit.
Même dans le cas d'héritage multiple ? Il me semblait que ça n'était plus vrai...
Vincent
-- vmime, une bibliothèque C++ sous licence GPL pour parser et générer des messages au format MIME : http://www.sourceforge.net/projects/vmime/
alexis.nikichine
Vincent Richard wrote:
Justement, c'est bien ce que je veux faire ! Le vecteur contient des élements de type A* (non changeable), et je suis sûr que ce sont des objets de type C.
si tu es sûr que tout est du C*, un static_cast<C*> suffit.
Même dans le cas d'héritage multiple ? Il me semblait que ça n'était plus vrai...
Il me semble qu'il y a problèmes avec l'héritage multiple. Si tu as une base ambigue:
class A { int membre_de_a; }; class B : public A { int membre_de_b; }; class C : public A { int membre_de_c; }; class D : public B,C { int membre_de_d; }; // A est deux fois base de D
Une disposition raisonnable de ton objet en mémoire est
position | d'un | contenu de la mémoire pointeur |
A* ou D* |-------- | int membre_de_a; (parent de B) |-------- || int membre_de_b; A* ou C* |-------- | int membre_de_a; (parent de C) |-------- || int membre_de_c; |-------- ||| int membre_de_d; |----------
Tu ne peux pas faire :
D* d; A* a = static_cast<A*>(b);
Tu ne peux en fait pas non plus faire de dynamic_cast; le problème est de savoir vers lequel des deux A bases de D tu veux avoir un pointeur. Donc faire
A* a = static_cast<A*>( static_cast<B*>(b) );
ou
A* a = static_cast<A*>( static_cast<C*>(b) );
pour préciser le "chemin que tu prend" dans l'arbre des bases de D (m'enfin, le static_cast<A*> est facultatif, on peut le laisser implicite)
Mais la on parle d'upcast (c'est déjà malheureux qu'il pose un problème, non ?). Pour downcaster, c'est encore pire.
Si tu as un A*, tu peux le convertir en B* ou C* par un static_cast direct. Mais il faut être sur de ton coup (ie que tu tiens bien le A* qui coincide avec le B*, ou vers le C* (ie scientificum: que ltu es bien sur du type dynamique de ton objet)). Après, tu pourras static_caster le pointeur obtenu en D*. Le seul moyen safe de t'en sortir c'est le dynamic_cast, quand A est polymorphe (ie contient une fonction membre virtuelle).
Si tu t'es dit "résolvons ce problème de class de base ambigue par un héritage virtuel":
class A { int membre_de_a; }; class B : virtual public A { int membre_de_b; }; class C : virtual public A { int membre_de_c; }; class D : public B,C { int membre_de_d; }; // A est une fois base de D
et le layout raisonnable devient (en omettant des pointeurs d'héritage virtuel renvoyant à des v-tables un peu mystiques)
D* ou B* |-------- || int membre_de_b; C* |-------- || int membre_de_c; |-------- ||| int membre_de_d; A* |---------- | int membre_de_a; |----------
alors la phrase précédente devient: "le seul moyen de t'en sortir est le dynamic_cast". Oui monsieur, bien que A ne soit plus ambigue, tu ne peux plus caster, ni dans un sens, ni dans l'autre par static_cast.
La raison... pfiouh, trop compliquée, vaut mieux admettre (meis quand on comprend, on voit une sorte de lumière :-). Voir un article de Stroustrup sur un exemple d'implémentation de l'héritage multiple http://www.cs.colorado.edu/~diwan/class-papers/mi.pdf), mais schématiquement, c'est que l'objet qui construit la base virtuelle A n'est plus ni B, ni C, mais la classe la plus dérivée de la hiérarchie (c'est bien ca ?). Ici D. Donc quand tu as un pointeur sur un A*, que tu veux convertir en D*, et bien ca dépend de si D est l'objet le plus dérivé de la hiérarchie. Si en fait ton D* ne pointait pas vers un authentique D, mais vers un E, classe dérivée de D, et bien c'est E qui doit construire A, et A n'est pas au même endroit dans l'objet, et ca tu n'en a a priori aucune idée lors de la compilation d'un cast de A vers D: Il suffit en effet de connaitre les definitions de A et D; E peut-etre encore meme pas compilé à ce moment la.
et le layout d'un E risquera d'être:
B* ou E* ou D* |-------- || int membre_de_b; C* ou E* ou D* |-------- || int membre_de_c; |-------- ||| int membre_de_d; |---------- ||||int membre_de_e; A* |---------- | int membre_de_a; |----------
Bon, j'arrête, je suis pas clair la. Il faut y réfléchir trois heures puis écrire un compilateur à titre d'exercice, et je pense que tout devient vraiment limpide.
Alexis.
PS. Bonjour Prof :-) Impressionné ?
Vincent Richard wrote:
Justement, c'est bien ce que je veux faire ! Le vecteur contient des
élements de type A* (non changeable), et je suis sûr que ce sont des
objets de type C.
si tu es sûr que tout est du C*, un static_cast<C*> suffit.
Même dans le cas d'héritage multiple ?
Il me semblait que ça n'était plus vrai...
Il me semble qu'il y a problèmes avec l'héritage multiple. Si tu as
une base ambigue:
class A { int membre_de_a; };
class B : public A { int membre_de_b; };
class C : public A { int membre_de_c; };
class D : public B,C { int membre_de_d; }; // A est deux fois base
de D
Une disposition raisonnable de ton objet en mémoire est
position |
d'un | contenu de la mémoire
pointeur |
A* ou D* |--------
| int membre_de_a; (parent de B)
|--------
|| int membre_de_b;
A* ou C* |--------
| int membre_de_a; (parent de C)
|--------
|| int membre_de_c;
|--------
||| int membre_de_d;
|----------
Tu ne peux pas faire :
D* d; A* a = static_cast<A*>(b);
Tu ne peux en fait pas non plus faire de dynamic_cast; le problème est
de savoir vers lequel des deux A bases de D tu veux avoir un pointeur.
Donc faire
A* a = static_cast<A*>( static_cast<B*>(b) );
ou
A* a = static_cast<A*>( static_cast<C*>(b) );
pour préciser le "chemin que tu prend" dans l'arbre des bases de D
(m'enfin, le static_cast<A*> est facultatif, on peut le laisser
implicite)
Mais la on parle d'upcast (c'est déjà malheureux qu'il pose un
problème, non ?). Pour downcaster, c'est encore pire.
Si tu as un A*, tu peux le convertir en B* ou C* par un static_cast
direct. Mais il faut être sur de ton coup (ie que tu tiens bien le A*
qui coincide avec le B*, ou vers le C* (ie scientificum: que ltu es
bien sur du type dynamique de ton objet)). Après, tu pourras
static_caster le pointeur obtenu en D*. Le seul moyen safe de t'en
sortir c'est le dynamic_cast, quand A est polymorphe (ie contient une
fonction membre virtuelle).
Si tu t'es dit "résolvons ce problème de class de base ambigue par un
héritage virtuel":
class A { int membre_de_a; };
class B : virtual public A { int membre_de_b; };
class C : virtual public A { int membre_de_c; };
class D : public B,C { int membre_de_d; }; // A est une fois base
de D
et le layout raisonnable devient (en omettant des pointeurs d'héritage
virtuel renvoyant à des v-tables un peu mystiques)
D* ou B* |--------
|| int membre_de_b;
C* |--------
|| int membre_de_c;
|--------
||| int membre_de_d;
A* |----------
| int membre_de_a;
|----------
alors la phrase précédente devient: "le seul moyen de t'en sortir est
le dynamic_cast". Oui monsieur, bien que A ne soit plus ambigue, tu ne
peux plus caster, ni dans un sens, ni dans l'autre par static_cast.
La raison... pfiouh, trop compliquée, vaut mieux admettre (meis quand
on comprend, on voit une sorte de lumière :-). Voir un article de
Stroustrup sur un exemple d'implémentation de l'héritage multiple
http://www.cs.colorado.edu/~diwan/class-papers/mi.pdf), mais
schématiquement, c'est que l'objet qui construit la base virtuelle A
n'est plus ni B, ni C, mais la classe la plus dérivée de la hiérarchie
(c'est bien ca ?). Ici D. Donc quand tu as un pointeur sur un A*, que
tu veux convertir en D*, et bien ca dépend de si D est l'objet le plus
dérivé de la hiérarchie. Si en fait ton D* ne pointait pas vers un
authentique D, mais vers un E, classe dérivée de D, et bien c'est E
qui doit construire A, et A n'est pas au même endroit dans l'objet, et
ca tu n'en a a priori aucune idée lors de la compilation d'un cast de
A vers D:
Il suffit en effet de connaitre les definitions de A et D; E peut-etre
encore meme pas compilé à ce moment la.
et le layout d'un E risquera d'être:
B* ou E* ou D* |--------
|| int membre_de_b;
C* ou E* ou D* |--------
|| int membre_de_c;
|--------
||| int membre_de_d;
|----------
||||int membre_de_e;
A* |----------
| int membre_de_a;
|----------
Bon, j'arrête, je suis pas clair la. Il faut y réfléchir trois heures
puis écrire un compilateur à titre d'exercice, et je pense que tout
devient vraiment limpide.
Justement, c'est bien ce que je veux faire ! Le vecteur contient des élements de type A* (non changeable), et je suis sûr que ce sont des objets de type C.
si tu es sûr que tout est du C*, un static_cast<C*> suffit.
Même dans le cas d'héritage multiple ? Il me semblait que ça n'était plus vrai...
Il me semble qu'il y a problèmes avec l'héritage multiple. Si tu as une base ambigue:
class A { int membre_de_a; }; class B : public A { int membre_de_b; }; class C : public A { int membre_de_c; }; class D : public B,C { int membre_de_d; }; // A est deux fois base de D
Une disposition raisonnable de ton objet en mémoire est
position | d'un | contenu de la mémoire pointeur |
A* ou D* |-------- | int membre_de_a; (parent de B) |-------- || int membre_de_b; A* ou C* |-------- | int membre_de_a; (parent de C) |-------- || int membre_de_c; |-------- ||| int membre_de_d; |----------
Tu ne peux pas faire :
D* d; A* a = static_cast<A*>(b);
Tu ne peux en fait pas non plus faire de dynamic_cast; le problème est de savoir vers lequel des deux A bases de D tu veux avoir un pointeur. Donc faire
A* a = static_cast<A*>( static_cast<B*>(b) );
ou
A* a = static_cast<A*>( static_cast<C*>(b) );
pour préciser le "chemin que tu prend" dans l'arbre des bases de D (m'enfin, le static_cast<A*> est facultatif, on peut le laisser implicite)
Mais la on parle d'upcast (c'est déjà malheureux qu'il pose un problème, non ?). Pour downcaster, c'est encore pire.
Si tu as un A*, tu peux le convertir en B* ou C* par un static_cast direct. Mais il faut être sur de ton coup (ie que tu tiens bien le A* qui coincide avec le B*, ou vers le C* (ie scientificum: que ltu es bien sur du type dynamique de ton objet)). Après, tu pourras static_caster le pointeur obtenu en D*. Le seul moyen safe de t'en sortir c'est le dynamic_cast, quand A est polymorphe (ie contient une fonction membre virtuelle).
Si tu t'es dit "résolvons ce problème de class de base ambigue par un héritage virtuel":
class A { int membre_de_a; }; class B : virtual public A { int membre_de_b; }; class C : virtual public A { int membre_de_c; }; class D : public B,C { int membre_de_d; }; // A est une fois base de D
et le layout raisonnable devient (en omettant des pointeurs d'héritage virtuel renvoyant à des v-tables un peu mystiques)
D* ou B* |-------- || int membre_de_b; C* |-------- || int membre_de_c; |-------- ||| int membre_de_d; A* |---------- | int membre_de_a; |----------
alors la phrase précédente devient: "le seul moyen de t'en sortir est le dynamic_cast". Oui monsieur, bien que A ne soit plus ambigue, tu ne peux plus caster, ni dans un sens, ni dans l'autre par static_cast.
La raison... pfiouh, trop compliquée, vaut mieux admettre (meis quand on comprend, on voit une sorte de lumière :-). Voir un article de Stroustrup sur un exemple d'implémentation de l'héritage multiple http://www.cs.colorado.edu/~diwan/class-papers/mi.pdf), mais schématiquement, c'est que l'objet qui construit la base virtuelle A n'est plus ni B, ni C, mais la classe la plus dérivée de la hiérarchie (c'est bien ca ?). Ici D. Donc quand tu as un pointeur sur un A*, que tu veux convertir en D*, et bien ca dépend de si D est l'objet le plus dérivé de la hiérarchie. Si en fait ton D* ne pointait pas vers un authentique D, mais vers un E, classe dérivée de D, et bien c'est E qui doit construire A, et A n'est pas au même endroit dans l'objet, et ca tu n'en a a priori aucune idée lors de la compilation d'un cast de A vers D: Il suffit en effet de connaitre les definitions de A et D; E peut-etre encore meme pas compilé à ce moment la.
et le layout d'un E risquera d'être:
B* ou E* ou D* |-------- || int membre_de_b; C* ou E* ou D* |-------- || int membre_de_c; |-------- ||| int membre_de_d; |---------- ||||int membre_de_e; A* |---------- | int membre_de_a; |----------
Bon, j'arrête, je suis pas clair la. Il faut y réfléchir trois heures puis écrire un compilateur à titre d'exercice, et je pense que tout devient vraiment limpide.