Pourquoi l'operateur -> const des smart ptr renvoie un non const ?
28 réponses
Aurelien Regat-Barrel
Bonjour,
Je suis surpris et déçu de constater que plusieurs pointeurs
intelligents (auto_ptr, boost) ont leur opérateur d'indirection défini
ainsi:
T* operator->() const;
alors que je me serais attendu à:
T* operator->();
const T* operator->() const;
ce qui permet d'avoir une erreur de compilation dans l'exemple qui suit:
class A
{
public:
void f1() const {}
void f2() {}
};
const auto_ptr<A> p( new A ); // <- const
p->f1();
p->f2(); // erreur de compilation attendue mais...
Du coup il faut passer par un
auto_ptr<const A>
qui ne peut pas être implicitement converti depuis un
auto_ptr<A>
:/
(cela dit ça marche avec boost, dans une certaine mesure). Je supose
qu'il y a une bonne raison à celà, mais je ne la devine pas. La
connaissez-vous ?
Il y a des limites de ce qu'on peut faire avec des conversions de type implicites. Est-ce que tu veux qu'on puisse convertir tous les vecteurs, du moment que le type contenu dans un se laisse convertir en type contenu dans l'autre. Que je puisse affeecter un vector<double> à un vector<char>, c-à-d :
Après réflexion, je réalise que c'est au niveau des références qu'est le problème. Ceci compile:
char a; char const & b = a;
mais pas ceci:
char * a = 0; char const * & b = a;
En fait, je ne comprends pas pourquoi.
A peu de chose près, j'imagine que c'est la même règle qui s'applique pour le refus d'initialiser une référence sur un template<T const> à partir d'un template<T>.
Du coup, j'ai une copie profonde que je n'ai peut-être pas voulue. Ça coûte cher, copier tout un vecteur.
Note bien que si tu veux la copie, tu peux toujours écrire :
Test2( AConstVect( v.begin(), v.end() ) ) ;
C'est la façon à appliquer la conversion explicite sur chaque élément du vecteur.
Je pense que je vais opter pour cette solution, avec un peu de chance le compilateur fera une bonne optimisation.
du coup, j'hésite à utiliser const dans mes pointeurs intelligents, et donc il perd son utilité au sein même de mes classes :-/
Je ne sais pas. Dans l'ensemble, je crois que boost::shared_ptr se comporte assez comme un pointeur brut à cet égard. Et j'avoue que ces restrictions ne m'ont jamais causé de problèmes. Les copies pûres et simples des vecteurs sont plutôt rares dans mon code ; ce qui arrive souvent, en revanche, ce sont des sous-ensembles -- une copie qui ne copie que certains éléments. Et dans ce cas-là, la solution la plus simple, c'est un itérateur filtrant de Boost, et le constructeur à deux itérateurs du vecteur. Mais alors, si le sous-ensemble doit être un vector< AConstPtr >, plutôt qu'un vector< APtr >, pas de problème -- parce que je me sers du constructeur à deux itérateurs, et que donc le compilateur cherche à faire la conversion élément par élément, plutôt que sur le vecteur complet.
-- Aurélien Regat-Barrel
Il y a des limites de ce qu'on peut faire avec des conversions
de type implicites. Est-ce que tu veux qu'on puisse convertir
tous les vecteurs, du moment que le type contenu dans un se
laisse convertir en type contenu dans l'autre. Que je puisse
affeecter un vector<double> à un vector<char>, c-à-d :
Après réflexion, je réalise que c'est au niveau des références qu'est le
problème. Ceci compile:
char a;
char const & b = a;
mais pas ceci:
char * a = 0;
char const * & b = a;
En fait, je ne comprends pas pourquoi.
A peu de chose près, j'imagine que c'est la même règle qui s'applique
pour le refus d'initialiser une référence sur un template<T const> à
partir d'un template<T>.
Du coup, j'ai une copie profonde que je n'ai peut-être pas
voulue. Ça coûte cher, copier tout un vecteur.
Note bien que si tu veux la copie, tu peux toujours écrire :
Test2( AConstVect( v.begin(), v.end() ) ) ;
C'est la façon à appliquer la conversion explicite sur chaque
élément du vecteur.
Je pense que je vais opter pour cette solution, avec un peu de chance le
compilateur fera une bonne optimisation.
du coup, j'hésite à utiliser const dans mes pointeurs
intelligents, et donc il perd son utilité au sein même de mes
classes :-/
Je ne sais pas. Dans l'ensemble, je crois que boost::shared_ptr
se comporte assez comme un pointeur brut à cet égard. Et j'avoue
que ces restrictions ne m'ont jamais causé de problèmes. Les
copies pûres et simples des vecteurs sont plutôt rares dans mon
code ; ce qui arrive souvent, en revanche, ce sont des
sous-ensembles -- une copie qui ne copie que certains éléments.
Et dans ce cas-là, la solution la plus simple, c'est un
itérateur filtrant de Boost, et le constructeur à deux
itérateurs du vecteur. Mais alors, si le sous-ensemble doit être
un vector< AConstPtr >, plutôt qu'un vector< APtr >, pas de
problème -- parce que je me sers du constructeur à deux
itérateurs, et que donc le compilateur cherche à faire la
conversion élément par élément, plutôt que sur le vecteur
complet.
Il y a des limites de ce qu'on peut faire avec des conversions de type implicites. Est-ce que tu veux qu'on puisse convertir tous les vecteurs, du moment que le type contenu dans un se laisse convertir en type contenu dans l'autre. Que je puisse affeecter un vector<double> à un vector<char>, c-à-d :
Après réflexion, je réalise que c'est au niveau des références qu'est le problème. Ceci compile:
char a; char const & b = a;
mais pas ceci:
char * a = 0; char const * & b = a;
En fait, je ne comprends pas pourquoi.
A peu de chose près, j'imagine que c'est la même règle qui s'applique pour le refus d'initialiser une référence sur un template<T const> à partir d'un template<T>.
Du coup, j'ai une copie profonde que je n'ai peut-être pas voulue. Ça coûte cher, copier tout un vecteur.
Note bien que si tu veux la copie, tu peux toujours écrire :
Test2( AConstVect( v.begin(), v.end() ) ) ;
C'est la façon à appliquer la conversion explicite sur chaque élément du vecteur.
Je pense que je vais opter pour cette solution, avec un peu de chance le compilateur fera une bonne optimisation.
du coup, j'hésite à utiliser const dans mes pointeurs intelligents, et donc il perd son utilité au sein même de mes classes :-/
Je ne sais pas. Dans l'ensemble, je crois que boost::shared_ptr se comporte assez comme un pointeur brut à cet égard. Et j'avoue que ces restrictions ne m'ont jamais causé de problèmes. Les copies pûres et simples des vecteurs sont plutôt rares dans mon code ; ce qui arrive souvent, en revanche, ce sont des sous-ensembles -- une copie qui ne copie que certains éléments. Et dans ce cas-là, la solution la plus simple, c'est un itérateur filtrant de Boost, et le constructeur à deux itérateurs du vecteur. Mais alors, si le sous-ensemble doit être un vector< AConstPtr >, plutôt qu'un vector< APtr >, pas de problème -- parce que je me sers du constructeur à deux itérateurs, et que donc le compilateur cherche à faire la conversion élément par élément, plutôt que sur le vecteur complet.
-- Aurélien Regat-Barrel
Falk Tannhäuser
Aurelien Regat-Barrel wrote:
Après réflexion, je réalise que c'est au niveau des référence s qu'est le problème. Ceci compile:
char a; char const & b = a;
mais pas ceci:
char * a = 0; char const * & b = a;
En fait, je ne comprends pas pourquoi.
char const c[] = "Parce que cela permettrait de casser la 'const cor rectness' !"; b = c; std::strcpy(a, "Et voilà :-p !");
Falk
Aurelien Regat-Barrel wrote:
Après réflexion, je réalise que c'est au niveau des référence s qu'est le
problème. Ceci compile:
char a;
char const & b = a;
mais pas ceci:
char * a = 0;
char const * & b = a;
En fait, je ne comprends pas pourquoi.
char const c[] = "Parce que cela permettrait de casser la 'const cor rectness' !";
b = c;
std::strcpy(a, "Et voilà :-p !");
char const c[] = "Parce que cela permettrait de casser la 'const correctness' !"; b = c; std::strcpy(a, "Et voilà :-p !");
Ok. Avec un autre const, ça marche:
char * a = 0; char const * const & b = a;
Du coup, je m'attends à pouvoir écrire:
std::vector<char> a; std::vector<char> const & b = a; // OK std::vector<char const> const & c = a; // :-(
mais je commence à trop nager pour pouvoir évaluer tout ce que cela implique.
-- Aurélien Regat-Barrel
Falk Tannhäuser
Aurelien Regat-Barrel wrote:
Ok. Avec un autre const, ça marche:
char * a = 0; char const * const & b = a;
Du coup, je m'attends à pouvoir écrire:
std::vector<char> a; std::vector<char> const & b = a; // OK std::vector<char const> const & c = a; // :-(
std::vector<char const> ne passe pas parce que le type qu'on met dans un vector doit supporter l'affectation.
En revanche, on peut imaginer des conversions qui seraient parfaitement sûres mais qui ne sont néanmoins pas supportées par le langage (sans doute personne ne les a pas proposées pour la standardisation parce que les cas où elles seraient utiles se présentent trop rarement dans la pratique). Par exemple std::vector<char*>::iterator ==> std::vector<char const*>::const_iterator std::vector<char>::iterator* ==> std::vector<char>::const_iterator const* (Note: Ces deux conversions passeront la compilation sur les implémentations où 'std::vector<T>::[[const_]]iterator' est défini comme 'T [[const]]*'. Ces implémentations semblent toutefois devenir de plus en plus rares ces jours-ci.)
Similairement, il pourrait aussi y avoir des conversions entre pointeurs sur les fonctions comme char* (*)() ===> char const* (*)() void (*)(char const*) ===> void (*)(char*)
Falk
Aurelien Regat-Barrel wrote:
Ok. Avec un autre const, ça marche:
char * a = 0;
char const * const & b = a;
Du coup, je m'attends à pouvoir écrire:
std::vector<char> a;
std::vector<char> const & b = a; // OK
std::vector<char const> const & c = a; // :-(
std::vector<char const> ne passe pas parce que
le type qu'on met dans un vector doit supporter
l'affectation.
En revanche, on peut imaginer des conversions qui seraient parfaitement
sûres mais qui ne sont néanmoins pas supportées par le langage (sans doute
personne ne les a pas proposées pour la standardisation parce que
les cas où elles seraient utiles se présentent trop rarement dans
la pratique). Par exemple
std::vector<char*>::iterator ==> std::vector<char const*>::const_iterator
std::vector<char>::iterator* ==> std::vector<char>::const_iterator const*
(Note: Ces deux conversions passeront la compilation sur les implémentations
où 'std::vector<T>::[[const_]]iterator' est défini comme 'T [[const]]*'. Ces
implémentations semblent toutefois devenir de plus en plus rares ces jours-ci.)
Similairement, il pourrait aussi y avoir des conversions entre pointeurs
sur les fonctions comme
char* (*)() ===> char const* (*)()
void (*)(char const*) ===> void (*)(char*)
std::vector<char> a; std::vector<char> const & b = a; // OK std::vector<char const> const & c = a; // :-(
std::vector<char const> ne passe pas parce que le type qu'on met dans un vector doit supporter l'affectation.
En revanche, on peut imaginer des conversions qui seraient parfaitement sûres mais qui ne sont néanmoins pas supportées par le langage (sans doute personne ne les a pas proposées pour la standardisation parce que les cas où elles seraient utiles se présentent trop rarement dans la pratique). Par exemple std::vector<char*>::iterator ==> std::vector<char const*>::const_iterator std::vector<char>::iterator* ==> std::vector<char>::const_iterator const* (Note: Ces deux conversions passeront la compilation sur les implémentations où 'std::vector<T>::[[const_]]iterator' est défini comme 'T [[const]]*'. Ces implémentations semblent toutefois devenir de plus en plus rares ces jours-ci.)
Similairement, il pourrait aussi y avoir des conversions entre pointeurs sur les fonctions comme char* (*)() ===> char const* (*)() void (*)(char const*) ===> void (*)(char*)
Falk
Fabien LE LEZ
On Fri, 28 Apr 2006 14:24:00 +0200, Fabien LE LEZ :
On Thu, 27 Apr 2006 22:03:36 +0200, Fabien LE LEZ :
Tu peux faire une classe qui se comporte comme tu le souhaites. L'appeler "pointeur" me choquerait effectivement.
En fait, je m'aperçois que je fais assez souvent des classes (non templates) de ce style, et que je les appelle généralement "handle", "gestionnaire", ou quelque chose d'approchant. Mais ces classes n'ont pas d'opérateur -> ou * : elles ont grosso modo les mêmes fonctions que la classe "de base".
Le modèle façade, en somme.
-- James Kanze Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
Fabien LE LEZ wrote:
On Thu, 27 Apr 2006 22:03:36 +0200, Fabien LE LEZ
<gramster@gramster.com>:
Tu peux faire une classe qui se comporte comme tu le souhaites.
L'appeler "pointeur" me choquerait effectivement.
En fait, je m'aperçois que je fais assez souvent des classes (non
templates) de ce style, et que je les appelle généralement "handle",
"gestionnaire", ou quelque chose d'approchant. Mais ces classes n'ont
pas d'opérateur -> ou * : elles ont grosso modo les mêmes fonctions
que la classe "de base".
Le modèle façade, en somme.
--
James Kanze kanze.james@neuf.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
On Thu, 27 Apr 2006 22:03:36 +0200, Fabien LE LEZ :
Tu peux faire une classe qui se comporte comme tu le souhaites. L'appeler "pointeur" me choquerait effectivement.
En fait, je m'aperçois que je fais assez souvent des classes (non templates) de ce style, et que je les appelle généralement "handle", "gestionnaire", ou quelque chose d'approchant. Mais ces classes n'ont pas d'opérateur -> ou * : elles ont grosso modo les mêmes fonctions que la classe "de base".
Le modèle façade, en somme.
-- James Kanze Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
Jean-Marc Bourguet
James Kanze writes:
Fabien LE LEZ wrote:
On Thu, 27 Apr 2006 22:03:36 +0200, Fabien LE LEZ :
Tu peux faire une classe qui se comporte comme tu le souhaites. L'appeler "pointeur" me choquerait effectivement.
En fait, je m'aperçois que je fais assez souvent des classes (non templates) de ce style, et que je les appelle généralement "handle", "gestionnaire", ou quelque chose d'approchant. Mais ces classes n'ont pas d'opérateur -> ou * : elles ont grosso modo les mêmes fonctions que la classe "de base".
Le modèle façade, en somme.
Proxy plutôt?
Il me semblait que le modèle façade, c'est proposer une interface unifiée à un ensemble de classes.
A+
-- Jean-Marc FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html Site de usenet-fr: http://www.usenet-fr.news.eu.org
James Kanze <kanze.james@neuf.fr> writes:
Fabien LE LEZ wrote:
On Thu, 27 Apr 2006 22:03:36 +0200, Fabien LE LEZ
<gramster@gramster.com>:
Tu peux faire une classe qui se comporte comme tu le souhaites.
L'appeler "pointeur" me choquerait effectivement.
En fait, je m'aperçois que je fais assez souvent des classes (non
templates) de ce style, et que je les appelle généralement "handle",
"gestionnaire", ou quelque chose d'approchant. Mais ces classes n'ont
pas d'opérateur -> ou * : elles ont grosso modo les mêmes fonctions
que la classe "de base".
Le modèle façade, en somme.
Proxy plutôt?
Il me semblait que le modèle façade, c'est proposer une
interface unifiée à un ensemble de classes.
A+
--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org
On Thu, 27 Apr 2006 22:03:36 +0200, Fabien LE LEZ :
Tu peux faire une classe qui se comporte comme tu le souhaites. L'appeler "pointeur" me choquerait effectivement.
En fait, je m'aperçois que je fais assez souvent des classes (non templates) de ce style, et que je les appelle généralement "handle", "gestionnaire", ou quelque chose d'approchant. Mais ces classes n'ont pas d'opérateur -> ou * : elles ont grosso modo les mêmes fonctions que la classe "de base".
Le modèle façade, en somme.
Proxy plutôt?
Il me semblait que le modèle façade, c'est proposer une interface unifiée à un ensemble de classes.
A+
-- Jean-Marc FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html Site de usenet-fr: http://www.usenet-fr.news.eu.org
James Kanze
Aurelien Regat-Barrel wrote:
[...]
Après réflexion, je réalise que c'est au niveau des références qu'est le problème. Ceci compile:
char a; char const & b = a;
mais pas ceci:
char * a = 0; char const * & b = a;
En fait, je ne comprends pas pourquoi.
Parce que :
char * a = 0 ; char const* & b = a ; char const c = 'x' ; b = &c ;
Et où est-ce que « a » pointe maintenant ?
A peu de chose près, j'imagine que c'est la même règle qui s'applique pour le refus d'initialiser une référence sur un template<T const> à partir d'un template<T>.
Pas tout à fait. T et T const sont deux types distincts. Apparenté, bien sûr, mais quand même des types différents. Ce qui fait qu'ils donnent lieu à des instances de template distinctes. Seulement, il n'y a pas d'apparenté entre les différentes instances d'un template, même s'il y en a entre leurs paramètres.
Dans la pratique, je ne vois pas comment ça pourrait ne pas être le cas. C'est facile de trouver des cas particuliers où ça serait commode si, mais essaie de formuler une règle générale et absolue qui s'appliquerait à tous les templates.
Du coup, j'ai une copie profonde que je n'ai peut-être pas voulue. Ça coûte cher, copier tout un vecteur.
Note bien que si tu veux la copie, tu peux toujours écrire :
Test2( AConstVect( v.begin(), v.end() ) ) ;
C'est la façon à appliquer la conversion explicite sur chaque élément du vecteur.
Je pense que je vais opter pour cette solution, avec un peu de chance le compilateur fera une bonne optimisation.
Disons que c'est l'inverse. Je ne sais pas à quelle optimisation tu t'attends, mais déjà avec la génération d'un temporaire, tu as (ou aurais) une copie profonde. Cette forme ne serait pas pire.
-- James Kanze Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
Aurelien Regat-Barrel wrote:
[...]
Après réflexion, je réalise que c'est au niveau des références
qu'est le problème. Ceci compile:
char a;
char const & b = a;
mais pas ceci:
char * a = 0;
char const * & b = a;
En fait, je ne comprends pas pourquoi.
Parce que :
char * a = 0 ;
char const* & b = a ;
char const c = 'x' ;
b = &c ;
Et où est-ce que « a » pointe maintenant ?
A peu de chose près, j'imagine que c'est la même règle qui
s'applique pour le refus d'initialiser une référence sur un
template<T const> à partir d'un template<T>.
Pas tout à fait. T et T const sont deux types distincts.
Apparenté, bien sûr, mais quand même des types différents. Ce
qui fait qu'ils donnent lieu à des instances de template
distinctes. Seulement, il n'y a pas d'apparenté entre les
différentes instances d'un template, même s'il y en a entre
leurs paramètres.
Dans la pratique, je ne vois pas comment ça pourrait ne pas être
le cas. C'est facile de trouver des cas particuliers où ça
serait commode si, mais essaie de formuler une règle générale et
absolue qui s'appliquerait à tous les templates.
Du coup, j'ai une copie profonde que je n'ai peut-être pas
voulue. Ça coûte cher, copier tout un vecteur.
Note bien que si tu veux la copie, tu peux toujours écrire :
Test2( AConstVect( v.begin(), v.end() ) ) ;
C'est la façon à appliquer la conversion explicite sur chaque
élément du vecteur.
Je pense que je vais opter pour cette solution, avec un peu de
chance le compilateur fera une bonne optimisation.
Disons que c'est l'inverse. Je ne sais pas à quelle optimisation
tu t'attends, mais déjà avec la génération d'un temporaire, tu
as (ou aurais) une copie profonde. Cette forme ne serait pas
pire.
--
James Kanze kanze.james@neuf.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
Après réflexion, je réalise que c'est au niveau des références qu'est le problème. Ceci compile:
char a; char const & b = a;
mais pas ceci:
char * a = 0; char const * & b = a;
En fait, je ne comprends pas pourquoi.
Parce que :
char * a = 0 ; char const* & b = a ; char const c = 'x' ; b = &c ;
Et où est-ce que « a » pointe maintenant ?
A peu de chose près, j'imagine que c'est la même règle qui s'applique pour le refus d'initialiser une référence sur un template<T const> à partir d'un template<T>.
Pas tout à fait. T et T const sont deux types distincts. Apparenté, bien sûr, mais quand même des types différents. Ce qui fait qu'ils donnent lieu à des instances de template distinctes. Seulement, il n'y a pas d'apparenté entre les différentes instances d'un template, même s'il y en a entre leurs paramètres.
Dans la pratique, je ne vois pas comment ça pourrait ne pas être le cas. C'est facile de trouver des cas particuliers où ça serait commode si, mais essaie de formuler une règle générale et absolue qui s'appliquerait à tous les templates.
Du coup, j'ai une copie profonde que je n'ai peut-être pas voulue. Ça coûte cher, copier tout un vecteur.
Note bien que si tu veux la copie, tu peux toujours écrire :
Test2( AConstVect( v.begin(), v.end() ) ) ;
C'est la façon à appliquer la conversion explicite sur chaque élément du vecteur.
Je pense que je vais opter pour cette solution, avec un peu de chance le compilateur fera une bonne optimisation.
Disons que c'est l'inverse. Je ne sais pas à quelle optimisation tu t'attends, mais déjà avec la génération d'un temporaire, tu as (ou aurais) une copie profonde. Cette forme ne serait pas pire.
-- James Kanze Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
James Kanze
Jean-Marc Bourguet wrote:
James Kanze writes:
Fabien LE LEZ wrote:
On Thu, 27 Apr 2006 22:03:36 +0200, Fabien LE LEZ :
Tu peux faire une classe qui se comporte comme tu le souhaites. L'appeler "pointeur" me choquerait effectivement.
En fait, je m'aperçois que je fais assez souvent des classes (non templates) de ce style, et que je les appelle généralement "handle", "gestionnaire", ou quelque chose d'approchant. Mais ces classes n'ont pas d'opérateur -> ou * : elles ont grosso modo les mêmes fonctions que la classe "de base".
Le modèle façade, en somme.
Proxy plutôt?
Il me semblait que le modèle façade, c'est proposer une interface unifiée à un ensemble de classes.
Intéressant. C'est la deuxième fois dernièrement où quelqu'un a dit proxy où j'aurais dit façade.
Dans ma tête, proxy, c'était toujours l'idiome du proxy que je connaissais bien avant le GoF : celui de l'item 30 dans « More Effective C++ », par exemple. Avec comme caractèristique fondamentale le fait qu'il soit construit par l'objet auquel il donne accès. Mais c'est vrai que le GoF donne surtout d'autres exemples, dont certains que j'appellerais aussi automatiquement un proxy -- surtout, par exemple, quand l'objet réel se trouve sur un autre système. En revanche, je n'aurais pas pensé à leur exemple de la création paresseuse comme un proxy. Mais pourquoi pas.
En ce qui concerne façade, le GoF cite bien le cas d'une interface unifiée à un ensemble de classes, comme utilisation typique. L'aspect fondamental m'en semble « fournir une interface simple à un sous-système complexe ». C'est vrai que ce qui rend le sous-système complexe, souvent, c'est le nombre de classes distinctes qui apparaissent au niveau de l'interface. Mais dès que la classe en question présente une abstraction plus simple, même si c'est d'une seule classe, je crois qu'on pourrait parler d'une façade.
En fin de comple, est-ce que ça ne dépendrait pas plutôt de pourquoi Fabien a créé cette classe intermédiaire ? Selon le cas, adaptateur, passerelle et décorateur pourrait aussi s'appliquait -- tous ces modèles ont comme caractèristique que le client accède à une classe intermédiaire, et non à la classe réele qui effectue la requête. (C'est aussi le cas de lettre/envéloppe, avec la particularité dans lettre/envéloppe que la classe effective dérive de la classe d'interface que voit le client.)
J'avoue que j'ai une tendance à utiliser le mot « façade » comme une désignation générique qui englobe tous ces modèles. Ce n'est peut-être pas une bonne idée.
-- James Kanze Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
Jean-Marc Bourguet wrote:
James Kanze <kanze.james@neuf.fr> writes:
Fabien LE LEZ wrote:
On Thu, 27 Apr 2006 22:03:36 +0200, Fabien LE LEZ
<gramster@gramster.com>:
Tu peux faire une classe qui se comporte comme tu le
souhaites. L'appeler "pointeur" me choquerait
effectivement.
En fait, je m'aperçois que je fais assez souvent des
classes (non templates) de ce style, et que je les appelle
généralement "handle", "gestionnaire", ou quelque chose
d'approchant. Mais ces classes n'ont pas d'opérateur ->
ou * : elles ont grosso modo les mêmes fonctions que la
classe "de base".
Le modèle façade, en somme.
Proxy plutôt?
Il me semblait que le modèle façade, c'est proposer une
interface unifiée à un ensemble de classes.
Intéressant. C'est la deuxième fois dernièrement où quelqu'un a
dit proxy où j'aurais dit façade.
Dans ma tête, proxy, c'était toujours l'idiome du proxy que je
connaissais bien avant le GoF : celui de l'item 30 dans « More
Effective C++ », par exemple. Avec comme caractèristique
fondamentale le fait qu'il soit construit par l'objet auquel il
donne accès. Mais c'est vrai que le GoF donne surtout d'autres
exemples, dont certains que j'appellerais aussi automatiquement
un proxy -- surtout, par exemple, quand l'objet réel se trouve
sur un autre système. En revanche, je n'aurais pas pensé à leur
exemple de la création paresseuse comme un proxy. Mais pourquoi
pas.
En ce qui concerne façade, le GoF cite bien le cas d'une
interface unifiée à un ensemble de classes, comme utilisation
typique. L'aspect fondamental m'en semble « fournir une
interface simple à un sous-système complexe ». C'est vrai
que ce qui rend le sous-système complexe, souvent, c'est le
nombre de classes distinctes qui apparaissent au niveau de
l'interface. Mais dès que la classe en question présente une
abstraction plus simple, même si c'est d'une seule classe, je
crois qu'on pourrait parler d'une façade.
En fin de comple, est-ce que ça ne dépendrait pas plutôt de
pourquoi Fabien a créé cette classe intermédiaire ? Selon le
cas, adaptateur, passerelle et décorateur pourrait aussi
s'appliquait -- tous ces modèles ont comme caractèristique que
le client accède à une classe intermédiaire, et non à la classe
réele qui effectue la requête. (C'est aussi le cas de
lettre/envéloppe, avec la particularité dans lettre/envéloppe
que la classe effective dérive de la classe d'interface que voit
le client.)
J'avoue que j'ai une tendance à utiliser le mot « façade » comme
une désignation générique qui englobe tous ces modèles. Ce n'est
peut-être pas une bonne idée.
--
James Kanze kanze.james@neuf.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
On Thu, 27 Apr 2006 22:03:36 +0200, Fabien LE LEZ :
Tu peux faire une classe qui se comporte comme tu le souhaites. L'appeler "pointeur" me choquerait effectivement.
En fait, je m'aperçois que je fais assez souvent des classes (non templates) de ce style, et que je les appelle généralement "handle", "gestionnaire", ou quelque chose d'approchant. Mais ces classes n'ont pas d'opérateur -> ou * : elles ont grosso modo les mêmes fonctions que la classe "de base".
Le modèle façade, en somme.
Proxy plutôt?
Il me semblait que le modèle façade, c'est proposer une interface unifiée à un ensemble de classes.
Intéressant. C'est la deuxième fois dernièrement où quelqu'un a dit proxy où j'aurais dit façade.
Dans ma tête, proxy, c'était toujours l'idiome du proxy que je connaissais bien avant le GoF : celui de l'item 30 dans « More Effective C++ », par exemple. Avec comme caractèristique fondamentale le fait qu'il soit construit par l'objet auquel il donne accès. Mais c'est vrai que le GoF donne surtout d'autres exemples, dont certains que j'appellerais aussi automatiquement un proxy -- surtout, par exemple, quand l'objet réel se trouve sur un autre système. En revanche, je n'aurais pas pensé à leur exemple de la création paresseuse comme un proxy. Mais pourquoi pas.
En ce qui concerne façade, le GoF cite bien le cas d'une interface unifiée à un ensemble de classes, comme utilisation typique. L'aspect fondamental m'en semble « fournir une interface simple à un sous-système complexe ». C'est vrai que ce qui rend le sous-système complexe, souvent, c'est le nombre de classes distinctes qui apparaissent au niveau de l'interface. Mais dès que la classe en question présente une abstraction plus simple, même si c'est d'une seule classe, je crois qu'on pourrait parler d'une façade.
En fin de comple, est-ce que ça ne dépendrait pas plutôt de pourquoi Fabien a créé cette classe intermédiaire ? Selon le cas, adaptateur, passerelle et décorateur pourrait aussi s'appliquait -- tous ces modèles ont comme caractèristique que le client accède à une classe intermédiaire, et non à la classe réele qui effectue la requête. (C'est aussi le cas de lettre/envéloppe, avec la particularité dans lettre/envéloppe que la classe effective dérive de la classe d'interface que voit le client.)
J'avoue que j'ai une tendance à utiliser le mot « façade » comme une désignation générique qui englobe tous ces modèles. Ce n'est peut-être pas une bonne idée.
-- James Kanze Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
Fabien LE LEZ
On Sun, 30 Apr 2006 12:09:39 +0200, James Kanze :
En fin de comple, est-ce que ça ne dépendrait pas plutôt de pourquoi Fabien a créé cette classe intermédiaire ?
Si tu parles de <news:, la réponse est dans le message : il s'agissait surtout de gérer la durée de vie de l'objet initial, à la façon d'un pointeur intelligent, mais sans le problème de "const" évoqué par l'OP -- un "HandleMachin const" ne permet pas de modifier le "Machin" contenu.
On Sun, 30 Apr 2006 12:09:39 +0200, James Kanze <kanze.james@neuf.fr>:
En fin de comple, est-ce que ça ne dépendrait pas plutôt de
pourquoi Fabien a créé cette classe intermédiaire ?
Si tu parles de <news:rn14521bff1k1iar4pvma6mbq7bmqpr4ah@4ax.com>, la
réponse est dans le message : il s'agissait surtout de gérer la durée
de vie de l'objet initial, à la façon d'un pointeur intelligent, mais
sans le problème de "const" évoqué par l'OP -- un "HandleMachin const"
ne permet pas de modifier le "Machin" contenu.
En fin de comple, est-ce que ça ne dépendrait pas plutôt de pourquoi Fabien a créé cette classe intermédiaire ?
Si tu parles de <news:, la réponse est dans le message : il s'agissait surtout de gérer la durée de vie de l'objet initial, à la façon d'un pointeur intelligent, mais sans le problème de "const" évoqué par l'OP -- un "HandleMachin const" ne permet pas de modifier le "Machin" contenu.