Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Classe abstraite ou pimpl ?

21 réponses
Avatar
Fabien LE LEZ
Bonjour,

Si je veux cacher les entrailles d'une classe, et ne montrer à son
utilisateur que les fonctions publiques, j'ai en gros le choix entre :

- l'idiome pimpl :

// .h
class C
{
public:
void f();
private:
class Implementation;
Implementation* pimpl;
};

// .cpp
void C::f()
{
pimpl-> f();
}

- l'idiome "classe de base abstraite" :

// .h
class C
{
public:
virtual void f()= 0;
};

// .cpp
class Implementation: public C
{
void f()
{
// Le code ici
}
}


Mon problème, c'est que je viens de m'apercevoir que je choisis entre
ces deux méthodes plus ou moins au hasard, suivant celle qui me vient
à l'esprit en premier.
Y a-t-il des critères rationnels pour choisir ?
Note : il s'agit généralement de classes à sémantique d'entité, non
copiables.

Merci d'avance...

10 réponses

1 2 3
Avatar
Stan
"Fabien LE LEZ" a écrit dans le message de news:

On Mon, 26 Sep 2005 15:16:35 +0200, "Stan" :

dans le premier cas, tu peux ne fournir qu'un fichier obj et un fichier
d'inclusion


Et pourquoi est-ce impossible dans le deuxième cas ?



dans le premier cas, tu peux ne fournir qu'un fichier obj.
Et, là, ne me dit pas que c'est possible dans le second ;-)

Comme ça, si tu fournis le fichier obj et une doc, l'implémentation
est dissimulée.

--
-Stan


Avatar
Fabien LE LEZ
On Tue, 27 Sep 2005 10:53:51 +0200, "Stan" :

dans le premier cas, tu peux ne fournir qu'un fichier obj.


Gni ?
T'es bien obligé de fournir un .h en plus, non ?

Et, là, ne me dit pas que c'est possible dans le second ;-)


Si, bien évidemment. Tu fournis le .h (avec la classe abstraite
uniquement) et un .obj (ou même une DLL : c'est comme ça que
fonctionnent les DLL "COM" sous Windows).

Avatar
kanze
Richard Delorme wrote:

Si je veux cacher les entrailles d'une classe, et ne montrer
à son utilisateur que les fonctions publiques,


Quel intérêt de cacher l'implémentation ?


Reduire les dépendences de complation (et aussi les temps de
compilation).

[...]

Mon problème, c'est que je viens de m'apercevoir que je
choisis entre ces deux méthodes plus ou moins au hasard,
suivant celle qui me vient à l'esprit en premier. Y a-t-il
des critères rationnels pour choisir ?



Je dirais que le critère principal, c'est de faire comme les
autres dans le projet. Si je fais un projet du début, j'utilise
en général l'idiome du pare-feu de compilation ; il cache même
le fait que tu t'en sers de l'utilisateur:-). C-à-d que si
plus tard, le profiler dit que le coût en est trop, tu peux le
remplacer par l'implémentation classique sans que l'utilisateur
ait à modifier son code. Et en passant, s'il introduit un niveau
supplémentaire d'indirection, il n'y a toujours pas d'appel
virtuel. Qui est cher sur certaines plateformes.

Mais l'avantage en est faible ; pas assez pour justifier changer
l'existant, ni même s'en écarter.

J'ai l'impression que cela ressemble au choix plus général
entre "is-a" (une classe B hérite d'une classe A) et "has-a"
(une classe B contient une classe A). En dehors de quelques
cas d'école, je ne crois pas qu'il y ait de critères
rationnels autre qu'empiriques pour choisir.


L'utilisation de l'interface et une (ou plusieurs) fonctions
usines permet à utliser en fait plusieurs classes différentes,
selon la fonction usine appelée et ses paramètres. L'utilisation
de l'idiome du pare-feu de compilation permet à remplacer la
classe par une implémentation classique plus tard, sans avoir à
modifier le code utilisateur.

Ils ne sont pas 100% identique.

--
James Kanze GABI Software
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


Avatar
kanze
Marc Boyer wrote:
Si je veux cacher les entrailles d'une classe, et ne montrer
à son utilisateur que les fonctions publiques, j'ai en gros
le choix entre :

- l'idiome pimpl :
- l'idiome "classe de base abstraite" :


J'aurais tendance à préférer la classe de base abstraite, car:
- l'indirection générée par la virtualité est gérée par le
compilateur, donc possiblement plus efficacement.


En théorie, peut-être. Mais pour le faire, il faut qu'il puisse
déterminer qu'il n'en a qu'une classe qui dérive de l'interface,
ou au moins qu'à l'endroit de l'appel, c'est toujours la même
classe qui implémente la fonction. Sinon, il est obligé à
utiliser l'appel virtuel classique. Qui, au moins sur certains
processeurs, peut coûter trois ou quatre fois plus cher qu'un
appel normal.

- le compilateur peut signaler quand on a oublié d'instancier
une méthode


Mais le linker est obligé à inclure toutes les fonctions dans
l'exécutable, même s'il y en a qui ne servent pas.

Mais bon, ce sont les avantages que je vois, je peux ne pas
voir les désavantages.


Il y en a un très clair : si par la suite, le profiler dit que
le coût des appels est un goulot d'étranglement, tu peux
remplacer le pare-feu de compilation par une implémentation
classique, avec même des fonctions inline, sans que
l'utilisateur soit obligé à changer quoique ce soit.

--
James Kanze GABI Software
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


Avatar
kanze
Aurelien Regat-Barrel wrote:
Moi je vois une assez grosse différence: la classe virtuelle
possède obligatoirement une sémantique de référence, avec
allocation dynamique (et il manque le code de la factory dans
ton code), alors que le pimpl peut être utilisé par valeur.


Surtout, la classe abstraite exige un delete quelque part -- ou
bien, un pointeur intelligent, ou bien, à charge de
l'utilisateur. Dans les deux cas, c'est une complication en plus
pour l'utilisateur.

Au moins, évidemment, d'utiliser un glaneur de cellules.

Au niveau de l'implémentation, je trouve le pimpl moins
agréable : gestion de l'allocation du pimpl,
déclaration/implémentation d'une classe dans un .cpp, ça fait
un code un peu plus lourd je trouve.


C'est effectivement un compromis : plus de complexité pour
l'implémenteur, moins pour l'utilisateur.

Bien que, pour être honnête, la différence n'est pas énorme, ni
dans un cas, ni dans l'autre.

--
James Kanze GABI Software
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

Avatar
Jean-Marc Bourguet
Fabien LE LEZ writes:

On 25 Sep 2005 02:39:06 -0700, "Charles Brossollet"
:

et je pense que du moment qu'il n'y a
pas de nécessité de faire hériter ta classe, c'est kif-kif.


De toutes façons, dans les deux cas le code client n'a pas à hériter
de ma classe.

Et l'idiome pimpl peut aussi gérer l'héritage : il suffit de faire
dériver une classe de la classe "Implementation".


L'heritage dans les deux cas n'est pas toujours tres simple a gerer,
en tout cas il y a des compromis a faire.

J'avais fais un resume des methodes possibles pour le cas de la classe
abstraite dans ce groupe il y a quelque temps en reponse a un message
de Luc si j'ai bonne memoire. Malheureusement, je n'ai toujours pas
eu le temps de l'etendre et d'en faire quelque chose de publiable.

J'ai aussi un resume des methodes pour pimpl dans un coin de ma
tete, mais la il n'est est meme jamais sorti.

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


Avatar
kanze
Jean-Marc Bourguet wrote:
Fabien LE LEZ writes:

On 25 Sep 2005 02:39:06 -0700, "Charles Brossollet"
:

et je pense que du moment qu'il n'y a pas de nécessité de
faire hériter ta classe, c'est kif-kif.


De toutes façons, dans les deux cas le code client n'a pas à
hériter de ma classe.

Et l'idiome pimpl peut aussi gérer l'héritage : il suffit de
faire dériver une classe de la classe "Implementation".


L'heritage dans les deux cas n'est pas toujours tres simple a
gerer, en tout cas il y a des compromis a faire.


Dans le cas du pare-feu de compilation, je ne vois pas de
problème. En fin de compte, le fait que tu utilises un pare-feu
de compilation est un détail de l'implémentation, tout à fait
transparent à l'utilisateur.

En revanche, je vois beaucoup moins d'intérêt de l'idiome si on
a de toute façon une classe de base abstraite. La plupart des
utilisateurs ne vont utiliser que la classe de base abstraite.

--
James Kanze GABI Software
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



Avatar
kanze
Jean-Marc Bourguet wrote:
Fabien LE LEZ writes:

On 25 Sep 2005 02:39:06 -0700, "Charles Brossollet"
:

et je pense que du moment qu'il n'y a pas de nécessité de
faire hériter ta classe, c'est kif-kif.


De toutes façons, dans les deux cas le code client n'a pas à
hériter de ma classe.

Et l'idiome pimpl peut aussi gérer l'héritage : il suffit de
faire dériver une classe de la classe "Implementation".


L'heritage dans les deux cas n'est pas toujours tres simple a
gerer, en tout cas il y a des compromis a faire.


Dans le cas du pare-feu de compilation, je ne vois pas de
problème. En fin de compte, le fait que tu utilises un pare-feu
de compilation est un détail de l'implémentation, tout à fait
transparent à l'utilisateur.

En revanche, je vois beaucoup moins d'intérêt de l'idiome si on
a de toute façon une classe de base abstraite. La plupart des
utilisateurs ne vont utiliser que la classe de base abstraite.

--
James Kanze GABI Software
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



Avatar
Jean-Marc Bourguet
"kanze" writes:

Jean-Marc Bourguet wrote:
Fabien LE LEZ writes:

On 25 Sep 2005 02:39:06 -0700, "Charles Brossollet"
:

et je pense que du moment qu'il n'y a pas de nécessité de
faire hériter ta classe, c'est kif-kif.


De toutes façons, dans les deux cas le code client n'a pas à
hériter de ma classe.

Et l'idiome pimpl peut aussi gérer l'héritage : il suffit de
faire dériver une classe de la classe "Implementation".


L'heritage dans les deux cas n'est pas toujours tres simple a
gerer, en tout cas il y a des compromis a faire.


Dans le cas du pare-feu de compilation, je ne vois pas de
problème. En fin de compte, le fait que tu utilises un pare-feu
de compilation est un détail de l'implémentation, tout à fait
transparent à l'utilisateur.


Je ne parlais pas de l'utilisation -- et encore -- mais de
l'implementation.

La difficulte dans les deux cas, c'est les hierarchies paralleles.
L'heritage multiple est une solution mais avec des effets parfois
surprenant dans la taille des objets.


En revanche, je vois beaucoup moins d'intérêt de l'idiome si on a de
toute façon une classe de base abstraite. La plupart des
utilisateurs ne vont utiliser que la classe de base abstraite.


L'interet du pare-feu pour une hierarchie ou il y a de l'heritage,
c'est naturellement de cacher l'implementation des classes dont les
utilisateurs de ton sous-systeme heritent. Si elles sont abstraites
sans membres donnees, d'accord c'est pas tres utile.

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




Avatar
Marc Boyer
kanze a écrit :
[SNIP des explications très claires]

Oui, en gros, moi je fait confiance au compilateur
car je le pense capable de faire mieux aue moi, et
toi tu connais assez le C++ et les compilateurs
pour savoir là ou tu seras capable de faire mieux.

Marc Boyer
--
À vélo, prendre une rue à contre-sens est moins dangeureux
que prendre un boulevard dans le sens légal. À qui la faute ?
1 2 3