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 ?
Une description du pattern :
http://www-sop.inria.fr/axis/cbrtools/manual/DesignPatterns/patronMethodes.s
html
Plutôt template qu'héritage? Peut-être qu'il est possible de faire une
version générique du pattern "template method", mais la forme utilisant
l'héritage me convient suffisamment.
L'héritage ne semble pas être un héritage d'implémentation puisque dans
l'exemple, la classe ancêtre(B) est retardée. D'après la classification des
héritages de B.Meyer, apparemment l'héritage est un héritage de
concrétisation ici.
J'ai une solution sans recopie de code: elle est un peu
lourde, mais ça marche. Elle utilise:
BInterface: interface abstraite de B
Une interface est une classe dont toutes les méthodes sont abstraites. Donc
une interface est toujours 'abstraite' normalement.
BStorage: implementation abstraite du stockage des éléments de B
Une implémentation abstraite?
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.
{
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
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
};
class BImpl : public B
{
public:
BImpl (list<int>& ints) : B (ints)
{
initialize (ints);//appel nécessaire à initialize
}
void do_append (int i)
{
ints.push_back (i);
}
private:
vector<int> ints;
};
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 ?
Une description du pattern :
http://www-sop.inria.fr/axis/cbrtools/manual/DesignPatterns/patronMethodes.s
html
Plutôt template qu'héritage? Peut-être qu'il est possible de faire une
version générique du pattern "template method", mais la forme utilisant
l'héritage me convient suffisamment.
L'héritage ne semble pas être un héritage d'implémentation puisque dans
l'exemple, la classe ancêtre(B) est retardée. D'après la classification des
héritages de B.Meyer, apparemment l'héritage est un héritage de
concrétisation ici.
J'ai une solution sans recopie de code: elle est un peu
lourde, mais ça marche. Elle utilise:
BInterface: interface abstraite de B
Une interface est une classe dont toutes les méthodes sont abstraites. Donc
une interface est toujours 'abstraite' normalement.
BStorage: implementation abstraite du stockage des éléments de B
Une implémentation abstraite?
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.
{
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
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
};
class BImpl : public B
{
public:
BImpl (list<int>& ints) : B (ints)
{
initialize (ints);//appel nécessaire à initialize
}
void do_append (int i)
{
ints.push_back (i);
}
private:
vector<int> ints;
};
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 ?
Une description du pattern :
http://www-sop.inria.fr/axis/cbrtools/manual/DesignPatterns/patronMethodes.s
html
Plutôt template qu'héritage? Peut-être qu'il est possible de faire une
version générique du pattern "template method", mais la forme utilisant
l'héritage me convient suffisamment.
L'héritage ne semble pas être un héritage d'implémentation puisque dans
l'exemple, la classe ancêtre(B) est retardée. D'après la classification des
héritages de B.Meyer, apparemment l'héritage est un héritage de
concrétisation ici.
J'ai une solution sans recopie de code: elle est un peu
lourde, mais ça marche. Elle utilise:
BInterface: interface abstraite de B
Une interface est une classe dont toutes les méthodes sont abstraites. Donc
une interface est toujours 'abstraite' normalement.
BStorage: implementation abstraite du stockage des éléments de B
Une implémentation abstraite?
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.
{
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
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
};
class BImpl : public B
{
public:
BImpl (list<int>& ints) : B (ints)
{
initialize (ints);//appel nécessaire à initialize
}
void do_append (int i)
{
ints.push_back (i);
}
private:
vector<int> ints;
};
Hugues Delorme wrote: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.
Disons que, avant d'être un AImpl, ton objet est, pendant un cours
instant, un A. En gros, quand on construit un AImpl, on construit
d'abord un A (appel a A::A()), puis on rajoute la surcouche de AImpl
(appel de AImpl::AImpl()).
Le problème, c'est que quand ton objet est un A() et pas
encore un AImpl, do_initialize n'existe pas...
C'est logique dans le sens ou AImpl::do_initialize va surement
mettre à jour des attributs spécifiques de AImpl, que A n'a pas (et
donc que AImpl n'a pas encore quand il n'est qu'un A).
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).
Hugues Delorme wrote:
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.
Disons que, avant d'être un AImpl, ton objet est, pendant un cours
instant, un A. En gros, quand on construit un AImpl, on construit
d'abord un A (appel a A::A()), puis on rajoute la surcouche de AImpl
(appel de AImpl::AImpl()).
Le problème, c'est que quand ton objet est un A() et pas
encore un AImpl, do_initialize n'existe pas...
C'est logique dans le sens ou AImpl::do_initialize va surement
mettre à jour des attributs spécifiques de AImpl, que A n'a pas (et
donc que AImpl n'a pas encore quand il n'est qu'un A).
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).
Hugues Delorme wrote: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.
Disons que, avant d'être un AImpl, ton objet est, pendant un cours
instant, un A. En gros, quand on construit un AImpl, on construit
d'abord un A (appel a A::A()), puis on rajoute la surcouche de AImpl
(appel de AImpl::AImpl()).
Le problème, c'est que quand ton objet est un A() et pas
encore un AImpl, do_initialize n'existe pas...
C'est logique dans le sens ou AImpl::do_initialize va surement
mettre à jour des attributs spécifiques de AImpl, que A n'a pas (et
donc que AImpl n'a pas encore quand il n'est qu'un A).
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).