Soit :
class A
{
public:
A () { do_initialize (); }
virtual void do_initialize () = 0;
};
class AImpl : public A
{
public:
AImpl () : A ()
{
}
void do_initialize ()
{
std::cout << "Initialization" << std::endl;
}
};
Compiler ce code avec g++ génère l'erreur suivante :
In constructor 'A::A()': abstract virtual 'virtual void A::do_initialize()'
called from constructor.
Il est impossible d'appeler des méthodes abstraites à partir d'un
constructeur de classe.
Pourtant, ceci pourrait être très pratique pour
définir un schéma d'initialisation commun à tous les descendants(une sorte
de patron de méthode pour la création d'instances) :
Comment détourner le problème?, car devoir recopier le même code
d'initialisation dans les constructeurs des descendants rend le texte
logiciel redondant.
Soit :
class A
{
public:
A () { do_initialize (); }
virtual void do_initialize () = 0;
};
class AImpl : public A
{
public:
AImpl () : A ()
{
}
void do_initialize ()
{
std::cout << "Initialization" << std::endl;
}
};
Compiler ce code avec g++ génère l'erreur suivante :
In constructor 'A::A()': abstract virtual 'virtual void A::do_initialize()'
called from constructor.
Il est impossible d'appeler des méthodes abstraites à partir d'un
constructeur de classe.
Pourtant, ceci pourrait être très pratique pour
définir un schéma d'initialisation commun à tous les descendants(une sorte
de patron de méthode pour la création d'instances) :
Comment détourner le problème?, car devoir recopier le même code
d'initialisation dans les constructeurs des descendants rend le texte
logiciel redondant.
Soit :
class A
{
public:
A () { do_initialize (); }
virtual void do_initialize () = 0;
};
class AImpl : public A
{
public:
AImpl () : A ()
{
}
void do_initialize ()
{
std::cout << "Initialization" << std::endl;
}
};
Compiler ce code avec g++ génère l'erreur suivante :
In constructor 'A::A()': abstract virtual 'virtual void A::do_initialize()'
called from constructor.
Il est impossible d'appeler des méthodes abstraites à partir d'un
constructeur de classe.
Pourtant, ceci pourrait être très pratique pour
définir un schéma d'initialisation commun à tous les descendants(une sorte
de patron de méthode pour la création d'instances) :
Comment détourner le problème?, car devoir recopier le même code
d'initialisation dans les constructeurs des descendants rend le texte
logiciel redondant.
Comment détourner le problème?, car devoir recopier le même code
d'initialisation dans les constructeurs des descendants rend le texte
logiciel redondant.
Peux-tu préciser le problème ? Car le mécanisme de construction
tel qu'il existe me semble déjà résoudre le problème tel qu'énoncé
ci dessus (mais surement pas tel qu'il se présente à toi).
Comment détourner le problème?, car devoir recopier le même code
d'initialisation dans les constructeurs des descendants rend le texte
logiciel redondant.
Peux-tu préciser le problème ? Car le mécanisme de construction
tel qu'il existe me semble déjà résoudre le problème tel qu'énoncé
ci dessus (mais surement pas tel qu'il se présente à toi).
Comment détourner le problème?, car devoir recopier le même code
d'initialisation dans les constructeurs des descendants rend le texte
logiciel redondant.
Peux-tu préciser le problème ? Car le mécanisme de construction
tel qu'il existe me semble déjà résoudre le problème tel qu'énoncé
ci dessus (mais surement pas tel qu'il se présente à toi).
Comment détourner le problème?, car devoir recopier le même code
d'initialisation dans les constructeurs des descendants rend le texte
logiciel redondant.
Peux-tu préciser le problème ? Car le mécanisme de construction
tel qu'il existe me semble déjà résoudre le problème tel qu'énoncé
ci dessus (mais surement pas tel qu'il se présente à toi).
Si je comprends bien, l'idée serait d'appeler automatiquement le
do_initialize() de l'objet dérivé à sa construction... ça peut paraitre
idiot ce que je vais dire, mais pourquoi ne pas faire le boulot de
do_initialize dans le constructeur de la classe dérivée ?! c'est quand meme
son role de construire correctement l'objet en tenant compte de ses
spécificités... :-?
Comment détourner le problème?, car devoir recopier le même code
d'initialisation dans les constructeurs des descendants rend le texte
logiciel redondant.
Peux-tu préciser le problème ? Car le mécanisme de construction
tel qu'il existe me semble déjà résoudre le problème tel qu'énoncé
ci dessus (mais surement pas tel qu'il se présente à toi).
Si je comprends bien, l'idée serait d'appeler automatiquement le
do_initialize() de l'objet dérivé à sa construction... ça peut paraitre
idiot ce que je vais dire, mais pourquoi ne pas faire le boulot de
do_initialize dans le constructeur de la classe dérivée ?! c'est quand meme
son role de construire correctement l'objet en tenant compte de ses
spécificités... :-?
Comment détourner le problème?, car devoir recopier le même code
d'initialisation dans les constructeurs des descendants rend le texte
logiciel redondant.
Peux-tu préciser le problème ? Car le mécanisme de construction
tel qu'il existe me semble déjà résoudre le problème tel qu'énoncé
ci dessus (mais surement pas tel qu'il se présente à toi).
Si je comprends bien, l'idée serait d'appeler automatiquement le
do_initialize() de l'objet dérivé à sa construction... ça peut paraitre
idiot ce que je vais dire, mais pourquoi ne pas faire le boulot de
do_initialize dans le constructeur de la classe dérivée ?! c'est quand meme
son role de construire correctement l'objet en tenant compte de ses
spécificités... :-?
Pourtant, ceci pourrait être très pratique pour
définir un schéma d'initialisation commun à tous les descendants(une
sorte
de patron de méthode pour la création d'instances) :
Ben, n'est-ce pas le rôle de l'appel à A::A() ?
Comment détourner le problème?, car devoir recopier le même code
d'initialisation dans les constructeurs des descendants rend le texte
logiciel redondant.
Peux-tu préciser le problème ? Car le mécanisme de construction
tel qu'il existe me semble déjà résoudre le problème tel qu'énoncé
ci dessus (mais surement pas tel qu'il se présente à toi).
Pourtant, ceci pourrait être très pratique pour
définir un schéma d'initialisation commun à tous les descendants(une
sorte
de patron de méthode pour la création d'instances) :
Ben, n'est-ce pas le rôle de l'appel à A::A() ?
Comment détourner le problème?, car devoir recopier le même code
d'initialisation dans les constructeurs des descendants rend le texte
logiciel redondant.
Peux-tu préciser le problème ? Car le mécanisme de construction
tel qu'il existe me semble déjà résoudre le problème tel qu'énoncé
ci dessus (mais surement pas tel qu'il se présente à toi).
Pourtant, ceci pourrait être très pratique pour
définir un schéma d'initialisation commun à tous les descendants(une
sorte
de patron de méthode pour la création d'instances) :
Ben, n'est-ce pas le rôle de l'appel à A::A() ?
Comment détourner le problème?, car devoir recopier le même code
d'initialisation dans les constructeurs des descendants rend le texte
logiciel redondant.
Peux-tu préciser le problème ? Car le mécanisme de construction
tel qu'il existe me semble déjà résoudre le problème tel qu'énoncé
ci dessus (mais surement pas tel qu'il se présente à toi).
Il est impossible d'appeler des méthodes abstraites à partir d'un
constructeur de classe.
Il est impossible d'appeler des méthodes abstraites à partir d'un
constructeur de classe.
Il est impossible d'appeler des méthodes abstraites à partir d'un
constructeur de classe.
Pourtant, ceci pourrait être très pratique pour
définir un schéma d'initialisation commun à tous les descendants(une
sortede patron de méthode pour la création d'instances) :
Ben, n'est-ce pas le rôle de l'appel à A::A() ?
Bien sûr, mais pouvoir appeler des méthodes retardées dans le constructeur
de la classe ancêtre permettrait d'avoir une initialisation plus générique.
Dans l'exemple que j'ai donné, définir une méthode do_initialize() retardée
ne semble pas justifié vu la simplicité du code. Mais pour des classes plus
complexes, l'utilité me semble très intéressante.
Un autre exemple pour tenter d'illustrer le propos :
class B
{
public:
B (list<int>& ints)//patron de méthode
{
list<int>::iterator i = ints.begin ();
while (i != ints.end ())
{
do_append (*i);//appel primitive
i++;
}
}
virtual void do_append (int i) = 0;//primitive ajout
virtual void do_prune (int index) = 0;//primitive suppression
...
};
class BImpl : public B
{
public:
BImpl (list<int>& ints) : B (ints) { }
void do_append (int i)//implémentation de la primitive
{
ints.push_back (i);
}
private:
vector<int> ints;
};
Dans cet exemple, la classe B laisse à ses descendants le choix du type de
conteneur(vector, list, map, etc.) pour copier 'ints' qu'elle reçoit en
paramètre. B utilise les méthode retardées 'do_(...)' pour manipuler le
conteneur réel.
Pourtant, ceci pourrait être très pratique pour
définir un schéma d'initialisation commun à tous les descendants(une
sorte
de patron de méthode pour la création d'instances) :
Ben, n'est-ce pas le rôle de l'appel à A::A() ?
Bien sûr, mais pouvoir appeler des méthodes retardées dans le constructeur
de la classe ancêtre permettrait d'avoir une initialisation plus générique.
Dans l'exemple que j'ai donné, définir une méthode do_initialize() retardée
ne semble pas justifié vu la simplicité du code. Mais pour des classes plus
complexes, l'utilité me semble très intéressante.
Un autre exemple pour tenter d'illustrer le propos :
class B
{
public:
B (list<int>& ints)//patron de méthode
{
list<int>::iterator i = ints.begin ();
while (i != ints.end ())
{
do_append (*i);//appel primitive
i++;
}
}
virtual void do_append (int i) = 0;//primitive ajout
virtual void do_prune (int index) = 0;//primitive suppression
...
};
class BImpl : public B
{
public:
BImpl (list<int>& ints) : B (ints) { }
void do_append (int i)//implémentation de la primitive
{
ints.push_back (i);
}
private:
vector<int> ints;
};
Dans cet exemple, la classe B laisse à ses descendants le choix du type de
conteneur(vector, list, map, etc.) pour copier 'ints' qu'elle reçoit en
paramètre. B utilise les méthode retardées 'do_(...)' pour manipuler le
conteneur réel.
Pourtant, ceci pourrait être très pratique pour
définir un schéma d'initialisation commun à tous les descendants(une
sortede patron de méthode pour la création d'instances) :
Ben, n'est-ce pas le rôle de l'appel à A::A() ?
Bien sûr, mais pouvoir appeler des méthodes retardées dans le constructeur
de la classe ancêtre permettrait d'avoir une initialisation plus générique.
Dans l'exemple que j'ai donné, définir une méthode do_initialize() retardée
ne semble pas justifié vu la simplicité du code. Mais pour des classes plus
complexes, l'utilité me semble très intéressante.
Un autre exemple pour tenter d'illustrer le propos :
class B
{
public:
B (list<int>& ints)//patron de méthode
{
list<int>::iterator i = ints.begin ();
while (i != ints.end ())
{
do_append (*i);//appel primitive
i++;
}
}
virtual void do_append (int i) = 0;//primitive ajout
virtual void do_prune (int index) = 0;//primitive suppression
...
};
class BImpl : public B
{
public:
BImpl (list<int>& ints) : B (ints) { }
void do_append (int i)//implémentation de la primitive
{
ints.push_back (i);
}
private:
vector<int> ints;
};
Dans cet exemple, la classe B laisse à ses descendants le choix du type de
conteneur(vector, list, map, etc.) pour copier 'ints' qu'elle reçoit en
paramètre. B utilise les méthode retardées 'do_(...)' pour manipuler le
conteneur réel.
Pourtant, ceci pourrait être très pratique pour
définir un schéma d'initialisation commun à tous les descendants(une
sortede patron de méthode pour la création d'instances) :
Ben, n'est-ce pas le rôle de l'appel à A::A() ?
Bien sûr, mais pouvoir appeler des méthodes retardées dans le constructeur
de la classe ancêtre permettrait d'avoir une initialisation plus générique.
Dans l'exemple que j'ai donné, définir une méthode do_initialize() retardée
ne semble pas justifié vu la simplicité du code. Mais pour des classes plus
complexes, l'utilité me semble très intéressante.
Un autre exemple pour tenter d'illustrer le propos :
class B
{
public:
B (list<int>& ints)//patron de méthode
{
list<int>::iterator i = ints.begin ();
while (i != ints.end ())
{
do_append (*i);//appel primitive
i++;
}
}
virtual void do_append (int i) = 0;//primitive ajout
virtual void do_prune (int index) = 0;//primitive suppression
...
};
class BImpl : public B
{
public:
BImpl (list<int>& ints) : B (ints) { }
void do_append (int i)//implémentation de la primitive
{
ints.push_back (i);
}
private:
vector<int> ints;
};
Dans cet exemple, la classe B laisse à ses descendants le choix du type de
conteneur(vector, list, map, etc.) pour copier 'ints' qu'elle reçoit en
paramètre. B utilise les méthode retardées 'do_(...)' pour manipuler le
conteneur réel.
Mais ce genre de collaboration(issu du pattern patron de méthode) entre
ancêtre et descendants est impossible en C++ (autres langages je ne sais
pas), si le patron est implémenté dans un constructeur.
Le problème est bien celui-ci : il est impossible d'appeler des méthodes
virtuelles pures d'une classe dans le code de ses constructeurs.
En considérant le code ci-dessus, je suis obligé d'effectuer la copie de
'ints' dans le constructeur de BImpl :
Pourtant, ceci pourrait être très pratique pour
définir un schéma d'initialisation commun à tous les descendants(une
sorte
de patron de méthode pour la création d'instances) :
Ben, n'est-ce pas le rôle de l'appel à A::A() ?
Bien sûr, mais pouvoir appeler des méthodes retardées dans le constructeur
de la classe ancêtre permettrait d'avoir une initialisation plus générique.
Dans l'exemple que j'ai donné, définir une méthode do_initialize() retardée
ne semble pas justifié vu la simplicité du code. Mais pour des classes plus
complexes, l'utilité me semble très intéressante.
Un autre exemple pour tenter d'illustrer le propos :
class B
{
public:
B (list<int>& ints)//patron de méthode
{
list<int>::iterator i = ints.begin ();
while (i != ints.end ())
{
do_append (*i);//appel primitive
i++;
}
}
virtual void do_append (int i) = 0;//primitive ajout
virtual void do_prune (int index) = 0;//primitive suppression
...
};
class BImpl : public B
{
public:
BImpl (list<int>& ints) : B (ints) { }
void do_append (int i)//implémentation de la primitive
{
ints.push_back (i);
}
private:
vector<int> ints;
};
Dans cet exemple, la classe B laisse à ses descendants le choix du type de
conteneur(vector, list, map, etc.) pour copier 'ints' qu'elle reçoit en
paramètre. B utilise les méthode retardées 'do_(...)' pour manipuler le
conteneur réel.
Mais ce genre de collaboration(issu du pattern patron de méthode) entre
ancêtre et descendants est impossible en C++ (autres langages je ne sais
pas), si le patron est implémenté dans un constructeur.
Le problème est bien celui-ci : il est impossible d'appeler des méthodes
virtuelles pures d'une classe dans le code de ses constructeurs.
En considérant le code ci-dessus, je suis obligé d'effectuer la copie de
'ints' dans le constructeur de BImpl :
Pourtant, ceci pourrait être très pratique pour
définir un schéma d'initialisation commun à tous les descendants(une
sortede patron de méthode pour la création d'instances) :
Ben, n'est-ce pas le rôle de l'appel à A::A() ?
Bien sûr, mais pouvoir appeler des méthodes retardées dans le constructeur
de la classe ancêtre permettrait d'avoir une initialisation plus générique.
Dans l'exemple que j'ai donné, définir une méthode do_initialize() retardée
ne semble pas justifié vu la simplicité du code. Mais pour des classes plus
complexes, l'utilité me semble très intéressante.
Un autre exemple pour tenter d'illustrer le propos :
class B
{
public:
B (list<int>& ints)//patron de méthode
{
list<int>::iterator i = ints.begin ();
while (i != ints.end ())
{
do_append (*i);//appel primitive
i++;
}
}
virtual void do_append (int i) = 0;//primitive ajout
virtual void do_prune (int index) = 0;//primitive suppression
...
};
class BImpl : public B
{
public:
BImpl (list<int>& ints) : B (ints) { }
void do_append (int i)//implémentation de la primitive
{
ints.push_back (i);
}
private:
vector<int> ints;
};
Dans cet exemple, la classe B laisse à ses descendants le choix du type de
conteneur(vector, list, map, etc.) pour copier 'ints' qu'elle reçoit en
paramètre. B utilise les méthode retardées 'do_(...)' pour manipuler le
conteneur réel.
Mais ce genre de collaboration(issu du pattern patron de méthode) entre
ancêtre et descendants est impossible en C++ (autres langages je ne sais
pas), si le patron est implémenté dans un constructeur.
Le problème est bien celui-ci : il est impossible d'appeler des méthodes
virtuelles pures d'une classe dans le code de ses constructeurs.
En considérant le code ci-dessus, je suis obligé d'effectuer la copie de
'ints' dans le constructeur de BImpl :
En général, quand je pense patron, la traduction C++ c'est plutôt
template qu'héritage. Le problème est pas abordé dans le "Design
Pattern" ?
Ceci dit, dans ce cas, j'ai l'impression que BImpl hérite de
B pour faire conjointement de l'héritage d'interface et de
l'héritage d'implémentation, non ?
J'ai une solution sans recopie de code: elle est un peu
lourde, mais ça marche. Elle utilise:
BInterface: interface abstraite de B
BStorage: implementation abstraite du stockage des éléments de B
BDefImpl: implementation par defaut de B, contient un BStorage&
BImpl: une implémentation particulière de B, elle a une sous-classe
qui implémente BStorage
Ca répond au problème général, peut-être qu'on peut simplifier.
#include <list>
#include <vector>
using std::list;
class BStorage {
public:
void fill(list<int>& ints){
list<int>::iterator i = ints.begin ();
while (i != ints.end () ) {
do_append (*i);//appel primitive
i++;
}
}
virtual void do_append (int i) = 0;
virtual ~BStorage() = 0;
};
class BInterface {
public:
BInterface (list<int>& ints);
virtual ~BInterface() = 0;
};
class BDefImpl {
BStorage& cont;
public:
BDefImpl(list<int>& ints, BStorage& theContainer):cont(theContainer){
cont.fill(ints);
}
};
class BImpl: public BInterface, private BDefImpl {
class BStorageImpl: public BStorage {
void do_append (int i){ vints.push_back(i); }
virtual ~BStorageImpl(){};
private:
std::vector<int> vints;
};
public:
BImpl(list<int>& ints): BInterface(ints),
BDefImpl(ints, *(new BStorageImpl)){};
virtual ~BImpl(){};
};
Marc Boyer
--
Lying for having sex or lying for making war? Trust US presidents :-(
En général, quand je pense patron, la traduction C++ c'est plutôt
template qu'héritage. Le problème est pas abordé dans le "Design
Pattern" ?
Ceci dit, dans ce cas, j'ai l'impression que BImpl hérite de
B pour faire conjointement de l'héritage d'interface et de
l'héritage d'implémentation, non ?
J'ai une solution sans recopie de code: elle est un peu
lourde, mais ça marche. Elle utilise:
BInterface: interface abstraite de B
BStorage: implementation abstraite du stockage des éléments de B
BDefImpl: implementation par defaut de B, contient un BStorage&
BImpl: une implémentation particulière de B, elle a une sous-classe
qui implémente BStorage
Ca répond au problème général, peut-être qu'on peut simplifier.
#include <list>
#include <vector>
using std::list;
class BStorage {
public:
void fill(list<int>& ints){
list<int>::iterator i = ints.begin ();
while (i != ints.end () ) {
do_append (*i);//appel primitive
i++;
}
}
virtual void do_append (int i) = 0;
virtual ~BStorage() = 0;
};
class BInterface {
public:
BInterface (list<int>& ints);
virtual ~BInterface() = 0;
};
class BDefImpl {
BStorage& cont;
public:
BDefImpl(list<int>& ints, BStorage& theContainer):cont(theContainer){
cont.fill(ints);
}
};
class BImpl: public BInterface, private BDefImpl {
class BStorageImpl: public BStorage {
void do_append (int i){ vints.push_back(i); }
virtual ~BStorageImpl(){};
private:
std::vector<int> vints;
};
public:
BImpl(list<int>& ints): BInterface(ints),
BDefImpl(ints, *(new BStorageImpl)){};
virtual ~BImpl(){};
};
Marc Boyer
--
Lying for having sex or lying for making war? Trust US presidents :-(
En général, quand je pense patron, la traduction C++ c'est plutôt
template qu'héritage. Le problème est pas abordé dans le "Design
Pattern" ?
Ceci dit, dans ce cas, j'ai l'impression que BImpl hérite de
B pour faire conjointement de l'héritage d'interface et de
l'héritage d'implémentation, non ?
J'ai une solution sans recopie de code: elle est un peu
lourde, mais ça marche. Elle utilise:
BInterface: interface abstraite de B
BStorage: implementation abstraite du stockage des éléments de B
BDefImpl: implementation par defaut de B, contient un BStorage&
BImpl: une implémentation particulière de B, elle a une sous-classe
qui implémente BStorage
Ca répond au problème général, peut-être qu'on peut simplifier.
#include <list>
#include <vector>
using std::list;
class BStorage {
public:
void fill(list<int>& ints){
list<int>::iterator i = ints.begin ();
while (i != ints.end () ) {
do_append (*i);//appel primitive
i++;
}
}
virtual void do_append (int i) = 0;
virtual ~BStorage() = 0;
};
class BInterface {
public:
BInterface (list<int>& ints);
virtual ~BInterface() = 0;
};
class BDefImpl {
BStorage& cont;
public:
BDefImpl(list<int>& ints, BStorage& theContainer):cont(theContainer){
cont.fill(ints);
}
};
class BImpl: public BInterface, private BDefImpl {
class BStorageImpl: public BStorage {
void do_append (int i){ vints.push_back(i); }
virtual ~BStorageImpl(){};
private:
std::vector<int> vints;
};
public:
BImpl(list<int>& ints): BInterface(ints),
BDefImpl(ints, *(new BStorageImpl)){};
virtual ~BImpl(){};
};
Marc Boyer
--
Lying for having sex or lying for making war? Trust US presidents :-(
Merci pour la solution, mais j'ai peur qu'elle ne complexifie les choses.
J'ai d'abord essayé une approche qui me semblait fonctionner, mais au final
non(si quelqu'un peut me dire pourquoi) :
class B
{
public:
B (list<int>& ints, B& b)//l'astuce est ici, la primitive sera appelée
pour b.
Merci pour la solution, mais j'ai peur qu'elle ne complexifie les choses.
J'ai d'abord essayé une approche qui me semblait fonctionner, mais au final
non(si quelqu'un peut me dire pourquoi) :
class B
{
public:
B (list<int>& ints, B& b)//l'astuce est ici, la primitive sera appelée
pour b.
Merci pour la solution, mais j'ai peur qu'elle ne complexifie les choses.
J'ai d'abord essayé une approche qui me semblait fonctionner, mais au final
non(si quelqu'un peut me dire pourquoi) :
class B
{
public:
B (list<int>& ints, B& b)//l'astuce est ici, la primitive sera appelée
pour b.
J'ai d'abord essayé une approche qui me semblait fonctionner, mais au final
non(si quelqu'un peut me dire pourquoi) :
class B
{
public:
B (list<int>& ints, B& b)//l'astuce est ici, la primitive sera appelée
pour b.
{
list<int>::iterator i = ints.begin ();
while (i != ints.end ())
{
b.do_append (*i);//do_append est appelée sur b.
i++;
}
}
virtual void do_append (int i) = 0;
};
class BImpl : public B
{
public:
BImpl (list<int>& ints) : B (ints, *this)//On passe *this au constructeur
de l'ancêtre
{
}
void do_append (int i)
{
ints.push_back (i);
}
private:
vector<int> ints;
};
Ce code compile sans problème, mais on obtient une erreur à l'exécution :
"pure virtual method called". Pourtant le polymorphisme devrait fonctionner
ici (appeler la version de BImpl et pas celle de B pour do_append).
Pour l'instant, j'ai opté pour un compromis acceptable entre duplication et
factorisation :
class B
{
public:
B (list<int>& ints) {}
void initialize (list<int>& ints)//le code du constructeur est ici
{
list<int>::iterator i = ints.begin ();
while (i != ints.end ())
{
do_append (*i);
i++;
}
}
virtual void do_append (int i) = 0;//primitive ajout
};
J'ai d'abord essayé une approche qui me semblait fonctionner, mais au final
non(si quelqu'un peut me dire pourquoi) :
class B
{
public:
B (list<int>& ints, B& b)//l'astuce est ici, la primitive sera appelée
pour b.
{
list<int>::iterator i = ints.begin ();
while (i != ints.end ())
{
b.do_append (*i);//do_append est appelée sur b.
i++;
}
}
virtual void do_append (int i) = 0;
};
class BImpl : public B
{
public:
BImpl (list<int>& ints) : B (ints, *this)//On passe *this au constructeur
de l'ancêtre
{
}
void do_append (int i)
{
ints.push_back (i);
}
private:
vector<int> ints;
};
Ce code compile sans problème, mais on obtient une erreur à l'exécution :
"pure virtual method called". Pourtant le polymorphisme devrait fonctionner
ici (appeler la version de BImpl et pas celle de B pour do_append).
Pour l'instant, j'ai opté pour un compromis acceptable entre duplication et
factorisation :
class B
{
public:
B (list<int>& ints) {}
void initialize (list<int>& ints)//le code du constructeur est ici
{
list<int>::iterator i = ints.begin ();
while (i != ints.end ())
{
do_append (*i);
i++;
}
}
virtual void do_append (int i) = 0;//primitive ajout
};
J'ai d'abord essayé une approche qui me semblait fonctionner, mais au final
non(si quelqu'un peut me dire pourquoi) :
class B
{
public:
B (list<int>& ints, B& b)//l'astuce est ici, la primitive sera appelée
pour b.
{
list<int>::iterator i = ints.begin ();
while (i != ints.end ())
{
b.do_append (*i);//do_append est appelée sur b.
i++;
}
}
virtual void do_append (int i) = 0;
};
class BImpl : public B
{
public:
BImpl (list<int>& ints) : B (ints, *this)//On passe *this au constructeur
de l'ancêtre
{
}
void do_append (int i)
{
ints.push_back (i);
}
private:
vector<int> ints;
};
Ce code compile sans problème, mais on obtient une erreur à l'exécution :
"pure virtual method called". Pourtant le polymorphisme devrait fonctionner
ici (appeler la version de BImpl et pas celle de B pour do_append).
Pour l'instant, j'ai opté pour un compromis acceptable entre duplication et
factorisation :
class B
{
public:
B (list<int>& ints) {}
void initialize (list<int>& ints)//le code du constructeur est ici
{
list<int>::iterator i = ints.begin ();
while (i != ints.end ())
{
do_append (*i);
i++;
}
}
virtual void do_append (int i) = 0;//primitive ajout
};