Bonjour,
Je voudrais *garantir* l'appel à une fonction virtuelle juste après la construction d'un
objet dérivé.
Ex :
class A
{
public :
A() {}
virtual Polish() { cout << "ptite couche de vernis" << endl;}
}
class B : public A
{
public :
B() {}
virtual Polish() { cout << "finalement, de la peinture plutot" << endl;}
}
Et je voudrais :
int main()
{
A a; // je voudrais appeller A::Polish
B b; // je voudrais appeller B::Polish (et pas A::Polish)
}
Pour l'instant, je suis obligé de faire ainsi :
Je déclare mes constructeurs protected, j'ajoute une fonction :
dans A :
static A* NewA() {A* pA=new A; pA->Polish(); return A;}
dans B :
static B* NewB() {B* pB=new B; pB->Polish(); return B;}
Et je construit mes objets via NewA ou NewB
En faisant ainsi, je suis obligé de rajouter une méthode NewX à chaque niveau d'héritage,
ce qui est lourd.
Et comme bien sûr "virtual static" n'existe pas...
|> La solution est intéressante pour une classe unique, mais il me |> semble qu'elle ne répond pas aux spécifications d'amerio dans |> le cas d'une hiérarchie de classes. Dans ce cas en effet, il va |> falloir non seulement implémenter chaque constructeur de la |> famille A, mais surtout ajouter dans la classe FinishA une clause |> friend pour chaque nouvelle classe de cette famille, ce qui risque |> de devenir envahissant (et fragile).
Pas du tout. Il faut bien implémenter un constructeur pour chaque classe, mais en ce qui concerne l'idiome, ce n'est que pour passer le paramètre, c-à-d :
Derived::Derived( FinishA const& a ) : Base( a ) { }
Et dans le cas où il l'oublie, le compilateur va râler, parce qu'il ne doit pas avoir de constructeur par défaut pour Base.
|> Plus grave, dans le cas suivant :
|> class A { ...}; |> class B : public A { ... }; |> int main() |> { |> B b; |> return 0; |> }
|> ...la méthode B::Polish est appellée deux fois : une |> première fois depuis A::A() (premier constructeur appelé |> puisque A est la classe de base), une deuxième fois depuis |> B::B().
D'où est-ce que tu vois ça ? Polish est appelé depuis le destructeur du temporaire. Alors, vue qu'il n'y a qu'une instance du temporaire, il n'est appelé qu'une seule fois.
(En passant, je me suis servi du méthode pendant longtemps dans mes classes FieldArray.)
-- James Kanze mailto: Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 11 rue de Rambouillet, 78460 Chevreuse, France +33 1 41 89 80 93
Christophe Brun <herode@club-internet.fr> writes:
|> Le 27 Sep 2003 18:26:13 +0200, James Kanze <kanze@alex.gabi-soft.fr>
|> a écrit:
|> > "amerio" <amerio@hotmail.com> writes:
|> > |> Je voudrais *garantir* l'appel à une fonction virtuelle
|> > |> juste après la construction d'un objet dérivé.
|> > [...]
|> > |> Et je voudrais :
|> > |> int main()
|> > |> {
|> > |> A a; // je voudrais appeller A::Polish
|> > |> B b; // je voudrais appeller B::Polish (et pas A::Polish)
|> > |> }
|> La solution est intéressante pour une classe unique, mais il me
|> semble qu'elle ne répond pas aux spécifications d'amerio dans
|> le cas d'une hiérarchie de classes. Dans ce cas en effet, il va
|> falloir non seulement implémenter chaque constructeur de la
|> famille A, mais surtout ajouter dans la classe FinishA une clause
|> friend pour chaque nouvelle classe de cette famille, ce qui risque
|> de devenir envahissant (et fragile).
Pas du tout. Il faut bien implémenter un constructeur pour chaque
classe, mais en ce qui concerne l'idiome, ce n'est que pour passer le
paramètre, c-à-d :
Derived::Derived( FinishA const& a )
: Base( a )
{
}
Et dans le cas où il l'oublie, le compilateur va râler, parce
qu'il ne doit pas avoir de constructeur par défaut pour Base.
|> Plus grave, dans le cas suivant :
|> class A { ...};
|> class B : public A { ... };
|> int main()
|> {
|> B b;
|> return 0;
|> }
|> ...la méthode B::Polish est appellée deux fois : une
|> première fois depuis A::A() (premier constructeur appelé
|> puisque A est la classe de base), une deuxième fois depuis
|> B::B().
D'où est-ce que tu vois ça ? Polish est appelé depuis le
destructeur du temporaire. Alors, vue qu'il n'y a qu'une instance du
temporaire, il n'est appelé qu'une seule fois.
(En passant, je me suis servi du méthode pendant longtemps dans mes
classes FieldArray.)
--
James Kanze mailto:kanze@gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France +33 1 41 89 80 93
|> La solution est intéressante pour une classe unique, mais il me |> semble qu'elle ne répond pas aux spécifications d'amerio dans |> le cas d'une hiérarchie de classes. Dans ce cas en effet, il va |> falloir non seulement implémenter chaque constructeur de la |> famille A, mais surtout ajouter dans la classe FinishA une clause |> friend pour chaque nouvelle classe de cette famille, ce qui risque |> de devenir envahissant (et fragile).
Pas du tout. Il faut bien implémenter un constructeur pour chaque classe, mais en ce qui concerne l'idiome, ce n'est que pour passer le paramètre, c-à-d :
Derived::Derived( FinishA const& a ) : Base( a ) { }
Et dans le cas où il l'oublie, le compilateur va râler, parce qu'il ne doit pas avoir de constructeur par défaut pour Base.
|> Plus grave, dans le cas suivant :
|> class A { ...}; |> class B : public A { ... }; |> int main() |> { |> B b; |> return 0; |> }
|> ...la méthode B::Polish est appellée deux fois : une |> première fois depuis A::A() (premier constructeur appelé |> puisque A est la classe de base), une deuxième fois depuis |> B::B().
D'où est-ce que tu vois ça ? Polish est appelé depuis le destructeur du temporaire. Alors, vue qu'il n'y a qu'une instance du temporaire, il n'est appelé qu'une seule fois.
(En passant, je me suis servi du méthode pendant longtemps dans mes classes FieldArray.)
-- James Kanze mailto: Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 11 rue de Rambouillet, 78460 Chevreuse, France +33 1 41 89 80 93
Christophe Brun
Le 28 Sep 2003 15:30:58 +0200, James Kanze a écrit:
Pas du tout. Il faut bien implémenter un constructeur pour chaque classe, mais en ce qui concerne l'idiome, ce n'est que pour passer le paramètre, c-à-d :
Derived::Derived( FinishA const& a ) : Base( a ) { }
Et dans le cas où il l'oublie, le compilateur va râler, parce qu'il ne doit pas avoir de constructeur par défaut pour Base.
Ok, autant pour moi ! Sur le test que j'avais (un peu hâtivement) fait d'après ton code, j'avais négligé de repasser le FinishA vers le constructeur de la classe de base (d'où l'objection qui suivait concernant l'appel en double) :
|> Plus grave, dans le cas suivant :
|> class A { ...}; |> class B : public A { ... }; |> int main() |> { |> B b; |> return 0; |> }
|> ...la méthode B::Polish est appellée deux fois : une |> première fois depuis A::A() (premier constructeur appelé |> puisque A est la classe de base), une deuxième fois depuis |> B::B().
D'où est-ce que tu vois ça ? Polish est appelé depuis le destructeur du temporaire. Alors, vue qu'il n'y a qu'une instance du temporaire, il n'est appelé qu'une seule fois.
(En passant, je me suis servi du méthode pendant longtemps dans mes classes FieldArray.)
Je retire dons mes objections ! :o)
-- Cordialement; Christophe Brun
Le 28 Sep 2003 15:30:58 +0200, James Kanze <kanze@alex.gabi-soft.fr> a
écrit:
Pas du tout. Il faut bien implémenter un constructeur pour chaque
classe, mais en ce qui concerne l'idiome, ce n'est que pour passer le
paramètre, c-à-d :
Derived::Derived( FinishA const& a )
: Base( a )
{
}
Et dans le cas où il l'oublie, le compilateur va râler, parce
qu'il ne doit pas avoir de constructeur par défaut pour Base.
Ok, autant pour moi ! Sur le test que j'avais (un peu hâtivement) fait
d'après ton code, j'avais négligé de repasser le FinishA vers le
constructeur de la classe de base (d'où l'objection qui suivait concernant
l'appel
en double) :
|> Plus grave, dans le cas suivant :
|> class A { ...};
|> class B : public A { ... };
|> int main()
|> {
|> B b;
|> return 0;
|> }
|> ...la méthode B::Polish est appellée deux fois : une
|> première fois depuis A::A() (premier constructeur appelé
|> puisque A est la classe de base), une deuxième fois depuis
|> B::B().
D'où est-ce que tu vois ça ? Polish est appelé depuis le
destructeur du temporaire. Alors, vue qu'il n'y a qu'une instance du
temporaire, il n'est appelé qu'une seule fois.
(En passant, je me suis servi du méthode pendant longtemps dans mes
classes FieldArray.)
Le 28 Sep 2003 15:30:58 +0200, James Kanze a écrit:
Pas du tout. Il faut bien implémenter un constructeur pour chaque classe, mais en ce qui concerne l'idiome, ce n'est que pour passer le paramètre, c-à-d :
Derived::Derived( FinishA const& a ) : Base( a ) { }
Et dans le cas où il l'oublie, le compilateur va râler, parce qu'il ne doit pas avoir de constructeur par défaut pour Base.
Ok, autant pour moi ! Sur le test que j'avais (un peu hâtivement) fait d'après ton code, j'avais négligé de repasser le FinishA vers le constructeur de la classe de base (d'où l'objection qui suivait concernant l'appel en double) :
|> Plus grave, dans le cas suivant :
|> class A { ...}; |> class B : public A { ... }; |> int main() |> { |> B b; |> return 0; |> }
|> ...la méthode B::Polish est appellée deux fois : une |> première fois depuis A::A() (premier constructeur appelé |> puisque A est la classe de base), une deuxième fois depuis |> B::B().
D'où est-ce que tu vois ça ? Polish est appelé depuis le destructeur du temporaire. Alors, vue qu'il n'y a qu'une instance du temporaire, il n'est appelé qu'une seule fois.
(En passant, je me suis servi du méthode pendant longtemps dans mes classes FieldArray.)
Je retire dons mes objections ! :o)
-- Cordialement; Christophe Brun
amerio
Voilà une solution avec template : [snip]
Parfait ! Ca marche ! Mais il reste un petit soucis. En effet, pour etre sur que mon Polish() est bien appelé, il faut que je passe obligatoirement par le Kit, donc mettre le constructeur en protected, et le Kit en friend. Et là, ca coince. Bon, déjà, j'ai sorti la classe registerer de Kit (pour simplifier le test). Si A::A() passe en protected, je dois alors ajouter dans A : friend class registerer<A> Jusque là, tout va bien. Mais si je met Polish() en protected (ce qu'il *doit* etre!) et que je rajoute une classe B: class A { public : friend class registerer<A>; protected: A() { cout << "A::A" << endl; } virtual void Polish() { cout << "A::Polish" << endl; } }; class B : public class A { public : friend class registerer<B>; protected: B() { cout << "B::B" << endl; } virtual void Polish() { cout << "B::Polish" << endl; } };
Et bien alors là, ca coince. A la compilation, VC6 refuse monKit.registerKit <B>("B");
error C2248: 'Polish' : cannot access protected member declared in class 'A'
[ Note : VC6 n'aime pas registerKit<X>("X") alors j'ai modifier ca en passant un argument bidon de type X* pour résoudre le template ]
Et là je ne comprend pas ! Je me doute d'un pb de compilo, mais qd même !
Alors peut être faut il déclarer tous les template possible friend ? Mais la syntaxe :
template <class T> friend class registerer<T>;
ne marche pas (error C2059: syntax error : '<end Parse>')
Pourtant, je croyais que c'etait la bonne syntaxe (enfin, c'est google qui le dis ;-) )
Un dernier coup de pouce ?
Voilà une solution avec template :
[snip]
Parfait ! Ca marche ! Mais il reste un petit soucis. En effet, pour etre sur que mon
Polish() est bien appelé, il faut que je passe obligatoirement par le Kit, donc mettre le
constructeur en protected, et le Kit en friend.
Et là, ca coince. Bon, déjà, j'ai sorti la classe registerer de Kit (pour simplifier le
test).
Si A::A() passe en protected, je dois alors ajouter dans A :
friend class registerer<A>
Jusque là, tout va bien.
Mais si je met Polish() en protected (ce qu'il *doit* etre!) et que je rajoute une classe
B:
class A
{
public :
friend class registerer<A>;
protected:
A() { cout << "A::A" << endl; }
virtual void Polish() { cout << "A::Polish" << endl; }
};
class B : public class A
{
public :
friend class registerer<B>;
protected:
B() { cout << "B::B" << endl; }
virtual void Polish() { cout << "B::Polish" << endl; }
};
Et bien alors là, ca coince.
A la compilation, VC6 refuse monKit.registerKit <B>("B");
error C2248: 'Polish' : cannot access protected member declared in class 'A'
[ Note : VC6 n'aime pas registerKit<X>("X") alors j'ai modifier ca en passant un argument
bidon de type X* pour résoudre le template ]
Et là je ne comprend pas ! Je me doute d'un pb de compilo, mais qd même !
Alors peut être faut il déclarer tous les template possible friend ?
Mais la syntaxe :
template <class T> friend class registerer<T>;
ne marche pas (error C2059: syntax error : '<end Parse>')
Pourtant, je croyais que c'etait la bonne syntaxe (enfin, c'est google qui le dis ;-) )
Parfait ! Ca marche ! Mais il reste un petit soucis. En effet, pour etre sur que mon Polish() est bien appelé, il faut que je passe obligatoirement par le Kit, donc mettre le constructeur en protected, et le Kit en friend. Et là, ca coince. Bon, déjà, j'ai sorti la classe registerer de Kit (pour simplifier le test). Si A::A() passe en protected, je dois alors ajouter dans A : friend class registerer<A> Jusque là, tout va bien. Mais si je met Polish() en protected (ce qu'il *doit* etre!) et que je rajoute une classe B: class A { public : friend class registerer<A>; protected: A() { cout << "A::A" << endl; } virtual void Polish() { cout << "A::Polish" << endl; } }; class B : public class A { public : friend class registerer<B>; protected: B() { cout << "B::B" << endl; } virtual void Polish() { cout << "B::Polish" << endl; } };
Et bien alors là, ca coince. A la compilation, VC6 refuse monKit.registerKit <B>("B");
error C2248: 'Polish' : cannot access protected member declared in class 'A'
[ Note : VC6 n'aime pas registerKit<X>("X") alors j'ai modifier ca en passant un argument bidon de type X* pour résoudre le template ]
Et là je ne comprend pas ! Je me doute d'un pb de compilo, mais qd même !
Alors peut être faut il déclarer tous les template possible friend ? Mais la syntaxe :
template <class T> friend class registerer<T>;
ne marche pas (error C2059: syntax error : '<end Parse>')
Pourtant, je croyais que c'etait la bonne syntaxe (enfin, c'est google qui le dis ;-) )
Un dernier coup de pouce ?
Vincent Richard
Mais si je met Polish() en protected (ce qu'il *doit* etre!) et que je rajoute une classe B:
[...]
Et bien alors là, ca coince. A la compilation, VC6 refuse monKit.registerKit <B>("B");
error C2248: 'Polish' : cannot access protected member declared in class 'A'
Remplace :
obj->Polish();
par :
static_cast<TYPE*>(obj)->Polish();
dans la fonction creator().
Ca devrait fonctionner. Chez moi (g++ 3.2.2), ça compile et s'exécute correctement après la modification.
Vincent
-- SL> Au fait elle est mieux ma signature maintenant ? Oui. T'enlève encore les conneries que t'as écrit dedans et c'est bon. -+- JB in <http://www.le-gnu.net> : Le neuneuttoyage par le vide -+-
Mais si je met Polish() en protected (ce qu'il *doit* etre!) et que je
rajoute une classe B:
[...]
Et bien alors là, ca coince.
A la compilation, VC6 refuse monKit.registerKit <B>("B");
error C2248: 'Polish' : cannot access protected member declared in class
'A'
Remplace :
obj->Polish();
par :
static_cast<TYPE*>(obj)->Polish();
dans la fonction creator().
Ca devrait fonctionner. Chez moi (g++ 3.2.2), ça compile et s'exécute
correctement après la modification.
Vincent
--
SL> Au fait elle est mieux ma signature maintenant ?
Oui. T'enlève encore les conneries que t'as écrit dedans et c'est bon.
-+- JB in <http://www.le-gnu.net> : Le neuneuttoyage par le vide -+-
Mais si je met Polish() en protected (ce qu'il *doit* etre!) et que je rajoute une classe B:
[...]
Et bien alors là, ca coince. A la compilation, VC6 refuse monKit.registerKit <B>("B");
error C2248: 'Polish' : cannot access protected member declared in class 'A'
Remplace :
obj->Polish();
par :
static_cast<TYPE*>(obj)->Polish();
dans la fonction creator().
Ca devrait fonctionner. Chez moi (g++ 3.2.2), ça compile et s'exécute correctement après la modification.
Vincent
-- SL> Au fait elle est mieux ma signature maintenant ? Oui. T'enlève encore les conneries que t'as écrit dedans et c'est bon. -+- JB in <http://www.le-gnu.net> : Le neuneuttoyage par le vide -+-
Ca vérifie quand même que TYPE dérive bien de A (grâce à la conversion implicite au "return").
Vincent
-- SL> Au fait elle est mieux ma signature maintenant ? Oui. T'enlève encore les conneries que t'as écrit dedans et c'est bon. -+- JB in <http://www.le-gnu.net> : Le neuneuttoyage par le vide -+-
Ca vérifie quand même que TYPE dérive bien de A (grâce à la conversion
implicite au "return").
Vincent
--
SL> Au fait elle est mieux ma signature maintenant ?
Oui. T'enlève encore les conneries que t'as écrit dedans et c'est bon.
-+- JB in <http://www.le-gnu.net> : Le neuneuttoyage par le vide -+-
Ca vérifie quand même que TYPE dérive bien de A (grâce à la conversion implicite au "return").
Vincent
-- SL> Au fait elle est mieux ma signature maintenant ? Oui. T'enlève encore les conneries que t'as écrit dedans et c'est bon. -+- JB in <http://www.le-gnu.net> : Le neuneuttoyage par le vide -+-
amerio
Mais si je met Polish() en protected (ce qu'il *doit* etre!) et que je rajoute une classe B:
[...]
Et bien alors là, ca coince. A la compilation, VC6 refuse monKit.registerKit <B>("B");
error C2248: 'Polish' : cannot access protected member declared in class 'A'
Remplace :
obj->Polish();
par :
static_cast<TYPE*>(obj)->Polish();
dans la fonction creator().
Ca devrait fonctionner. Chez moi (g++ 3.2.2), ça compile et s'exécute correctement après la modification.
Exact ! et encore merci :-)
N'empeche, je comprend pas pourquoi il a besoin du cast. (j'aime pas copypaster sans comprendre) registerer<A> est ami de A (donc peut acceder a A::Polish) registerer<B> est ami de B (donc peut acceder a B::Polish) Mais visiblement le compilo semble vouloir que registerer<B> puisse acceder a A::Polish aussi ?!? Le cast force le compilo a voir un 'B' et plus un 'A'. Mais pourquoi en a t on besoin ?
Mais si je met Polish() en protected (ce qu'il *doit* etre!) et que je
rajoute une classe B:
[...]
Et bien alors là, ca coince.
A la compilation, VC6 refuse monKit.registerKit <B>("B");
error C2248: 'Polish' : cannot access protected member declared in class
'A'
Remplace :
obj->Polish();
par :
static_cast<TYPE*>(obj)->Polish();
dans la fonction creator().
Ca devrait fonctionner. Chez moi (g++ 3.2.2), ça compile et s'exécute
correctement après la modification.
Exact ! et encore merci :-)
N'empeche, je comprend pas pourquoi il a besoin du cast.
(j'aime pas copypaster sans comprendre)
registerer<A> est ami de A (donc peut acceder a A::Polish)
registerer<B> est ami de B (donc peut acceder a B::Polish)
Mais visiblement le compilo semble vouloir que registerer<B> puisse acceder a A::Polish
aussi ?!?
Le cast force le compilo a voir un 'B' et plus un 'A'. Mais pourquoi en a t on besoin ?
Mais si je met Polish() en protected (ce qu'il *doit* etre!) et que je rajoute une classe B:
[...]
Et bien alors là, ca coince. A la compilation, VC6 refuse monKit.registerKit <B>("B");
error C2248: 'Polish' : cannot access protected member declared in class 'A'
Remplace :
obj->Polish();
par :
static_cast<TYPE*>(obj)->Polish();
dans la fonction creator().
Ca devrait fonctionner. Chez moi (g++ 3.2.2), ça compile et s'exécute correctement après la modification.
Exact ! et encore merci :-)
N'empeche, je comprend pas pourquoi il a besoin du cast. (j'aime pas copypaster sans comprendre) registerer<A> est ami de A (donc peut acceder a A::Polish) registerer<B> est ami de B (donc peut acceder a B::Polish) Mais visiblement le compilo semble vouloir que registerer<B> puisse acceder a A::Polish aussi ?!? Le cast force le compilo a voir un 'B' et plus un 'A'. Mais pourquoi en a t on besoin ?
Vincent Richard
N'empeche, je comprend pas pourquoi il a besoin du cast. (j'aime pas copypaster sans comprendre) registerer<A> est ami de A (donc peut acceder a A::Polish) registerer<B> est ami de B (donc peut acceder a B::Polish) Mais visiblement le compilo semble vouloir que registerer<B> puisse acceder a A::Polish aussi ?!?
C'est bien ce qu'on fait, non ?
A* obj = new B; obj->Polish();
On appelle bien Polish() sur un objet de type A, mais la virtualité fait que ça sera B::Polish qui sera appelée (mais le compilateur ne le sait pas, pour lui, c'est A::Polish qui semble appelée).
Mais cf. mon autre post de 18h31 pour éviter le static_cast<>.
Vincent
-- SL> Au fait elle est mieux ma signature maintenant ? Oui. T'enlève encore les conneries que t'as écrit dedans et c'est bon. -+- JB in <http://www.le-gnu.net> : Le neuneuttoyage par le vide -+-
N'empeche, je comprend pas pourquoi il a besoin du cast.
(j'aime pas copypaster sans comprendre)
registerer<A> est ami de A (donc peut acceder a A::Polish)
registerer<B> est ami de B (donc peut acceder a B::Polish)
Mais visiblement le compilo semble vouloir que registerer<B> puisse
acceder a A::Polish aussi ?!?
C'est bien ce qu'on fait, non ?
A* obj = new B;
obj->Polish();
On appelle bien Polish() sur un objet de type A, mais la virtualité
fait que ça sera B::Polish qui sera appelée (mais le compilateur ne
le sait pas, pour lui, c'est A::Polish qui semble appelée).
Mais cf. mon autre post de 18h31 pour éviter le static_cast<>.
Vincent
--
SL> Au fait elle est mieux ma signature maintenant ?
Oui. T'enlève encore les conneries que t'as écrit dedans et c'est bon.
-+- JB in <http://www.le-gnu.net> : Le neuneuttoyage par le vide -+-
N'empeche, je comprend pas pourquoi il a besoin du cast. (j'aime pas copypaster sans comprendre) registerer<A> est ami de A (donc peut acceder a A::Polish) registerer<B> est ami de B (donc peut acceder a B::Polish) Mais visiblement le compilo semble vouloir que registerer<B> puisse acceder a A::Polish aussi ?!?
C'est bien ce qu'on fait, non ?
A* obj = new B; obj->Polish();
On appelle bien Polish() sur un objet de type A, mais la virtualité fait que ça sera B::Polish qui sera appelée (mais le compilateur ne le sait pas, pour lui, c'est A::Polish qui semble appelée).
Mais cf. mon autre post de 18h31 pour éviter le static_cast<>.
Vincent
-- SL> Au fait elle est mieux ma signature maintenant ? Oui. T'enlève encore les conneries que t'as écrit dedans et c'est bon. -+- JB in <http://www.le-gnu.net> : Le neuneuttoyage par le vide -+-