OVH Cloud OVH Cloud

Forme canonique de Coplien et poymorphisme

137 réponses
Avatar
Y a n n R e n a r d
Bonjour à tous,

tout d'abord, merci à tous pour ce newsgroup très instructif, c'est la
première fois que je poste, alors je vais essayer de ne pas me faire
incendier par Fabien ;) :p

Voila mon problème : j'ai un client qui souhaite que toutes les classes
définies dans le projet sur lequel je travaille soient sous la forme
canonique de Coplien (pour ceux qui ne connaissent pas : constructeur
par défaut, constructeur par copie, operateur d'affectation et
destructeur virtuel ou non). Pour que ce soit plus clean (selon eux), le
client demande que toutes les classes dérivent d'une classe de base...
Sauf que je ne vois pas du tout l'intéret de faire ca car je ne vois pas
en quoi ca force à respecter la forme canonique de Coplien.

Concernant l'operateur d'affectation, si je le met virtuel (est ce
quelque chose de propre ?!) comment puis je faire en sorte que les
classes filles utilisent un opérateur correct ? cast dynamique ?

Concernant l'operateur de recopie, là, je vois pas du tout du tout du
tout :)

Bref, je comprends pas du tout cette demande, si quelqu'un pense que
c'est raisonable ou explicable, je veux bien une précision sur l'utilité...

Pour terminer, il faut aussi déporter le code du constructeur et du
destructeur dans une fonction séparée, et là, je comprends plus du
tout... genre quelque chose comme ca :

T::T(void)
{
construct();
}

T::~T(void)
{
destruct();
}

void T::construct(void)
{
// ...
}

void T::destruct(void)
{
// ...
}

Voila voila, votre avis est le bienvenu :)
Merci d'avance,

Yann Renard

10 réponses

Avatar
K. Ahausse
"Jean-Marc Bourguet" a écrit dans le message de
news:
"K. Ahausse" writes:


Je ne vois pas ce que les virtuelles pures viennent faire ici.


Oups, pardon. Je croyais qu'il était question de l'appel d'une fonction
virtuelle pure (définie dans la classe dérivée) à partir du constructeur de
la classe de base.

Pour citer, James Kanze : "La seule chose à se rappeler, c'est que pendant
l'execution d'un constructeur ou d'un destructeur de la class C, le type
dynamique est C. On ne tient pas compte du futur (dans le cas d'un
constructeur) ..."

Et donc : a la construction de la classe de base, la fonction virtuelle
pure n'étant pas encore définie, on ne peut faire appel à elle.



Ce sont des membres virtuels comme les autres a deux differences pres:
- les descendants instanciables sont obliges de les supplanter
- on peut ne pas les definir (a l'exception du destructeur)

Tu confonds peut-etre avec l'autre partie de la discussion avec
Laurent ou j'indique qu'on peut arriver a cause de ces regles sur le
type dynamique durant l'execution des constructeurs et des
destructeurs a appeler un membre virtuel pur sans le faire de maniere
qualifiee (on peut naturellement le faire de maniere qualifiee dans
d'autres contextes).


Là je n'ai pas compris .

Avatar
Gabriel Dos Reis
writes:

| > En C++, il a toujours été possible d'appeler une fonction virtuelle
| > dans un constructeur ou destructeur ; seulement le mécanisme de
| > résolution n'est pas dynamique mais statique.
|
| Non plus.

Huh ??

| L'appel d'une fonction virtuelle dans un constructeur ou dans
| un destructeur marche exactement comme l'appel d'une fonction virtuelle
| n'importe où ailleurs : il se résoud selon le type dynamique de l'objet
| (et non le type statique du pointeur). La seule chose à se rappeler,
| c'est que pendant l'execution d'un constructeur ou d'un destructeur de
| la class C, le type dynamique est C.

et en quoi est-ce « non plus » ?

| On ne tient pas compte du futur
| (dans le cas d'un constructeur) ni du passé (dans le cas d'un
| destructeur).
|
| J'avoue ne pas comprendre tous ces histoires. Pour une fois qu'en C++,

J'avoue ne pas saisir ton « non plus ».


| on a quelque chose de simple, clair et cohérent, les gens s'en
| plaignent, et cherche une complexité qu'il n'y est pas. Le type
| dynamique d'un objet change lors de sa construction et de sa
| destruction. C'est tout ce qu'il faut savoir pour tout comprendre.

Le type dynamique d'un objet ne change pas, ni dans le constructeur,
ni dans le destructeur. Il est ce qu'il est. Point.

Les constructeurs de classes de base contruisent les sous-objets
correspondant. C'est tout.

| (Enfin, c'est comme ça que je le comprends, et c'est comme ça qu'il me
| semble le plus clair.)

Vu que le type dynamique ne change pas, je trouve cela un peu curieux
que tu le trouve plus clair ;-p

-- Gaby
Avatar
Gabriel Dos Reis
"K. Ahausse" writes:

| "Jean-Marc Bourguet" a écrit dans le message de
| news:
| > "K. Ahausse" writes:
| >
| >
| > Je ne vois pas ce que les virtuelles pures viennent faire ici.
|
| Oups, pardon. Je croyais qu'il était question de l'appel d'une fonction
| virtuelle pure (définie dans la classe dérivée) à partir du constructeur de
| la classe de base.
|
| Pour citer, James Kanze : "La seule chose à se rappeler, c'est que pendant
| l'execution d'un constructeur ou d'un destructeur de la class C, le type
| dynamique est C. On ne tient pas compte du futur (dans le cas d'un
| constructeur) ..."
|
| Et donc : a la construction de la classe de base, la fonction virtuelle
| pure n'étant pas encore définie, on ne peut faire appel à elle.

Mais c'est aussi un non-sense :

#include <iostream>

struct A {
virtual void f() = 0;
virtual ~A() { f(); }
};

void A::f() { std::cout << "coucou !n"; }

-- Gaby
Avatar
K. Ahausse
"Gabriel Dos Reis" a écrit dans le message de
news:
"K. Ahausse" writes:

| "Jean-Marc Bourguet" a écrit dans le message de
| news:
| > "K. Ahausse" writes:
| >
| >
| > Je ne vois pas ce que les virtuelles pures viennent faire ici.
|
| Oups, pardon. Je croyais qu'il était question de l'appel d'une fonction
| virtuelle pure (définie dans la classe dérivée) à partir du constructeur
de

| la classe de base.
|
| Pour citer, James Kanze : "La seule chose à se rappeler, c'est que
pendant

| l'execution d'un constructeur ou d'un destructeur de la class C, le type
| dynamique est C. On ne tient pas compte du futur (dans le cas d'un
| constructeur) ..."
|
| Et donc : a la construction de la classe de base, la fonction virtuelle
| pure n'étant pas encore définie, on ne peut faire appel à elle.

Mais c'est aussi un non-sense :

#include <iostream>

struct A {
virtual void f() = 0;
virtual ~A() { f(); }
};

void A::f() { std::cout << "coucou !n"; }


Oui, je vois, tu as raison aussi :-)
Merci pour l'info.

Avatar
kanze
Jean-Marc Bourguet wrote in message
news:...
"K. Ahausse" writes:

Comme le faisait remarquer Jean-Marc Bourguet, le problème provient,
peut-être, de la confusion entre fonction virtuelle et fonction
virtuelle pure.


Je ne vois pas ce que les virtuelles pures viennent faire ici.


Moi non plus.

Ce sont des membres virtuels comme les autres a deux differences pres:
- les descendants instanciables sont obliges de les supplanter
- on peut ne pas les definir (a l'exception du destructeur)


D'où est-ce que tu tiens ça ? On peut très bien définir n'importe quelle
fonction virtuelle. (Quel en est l'intérêt est une autre question.)

En fait, à peu près la seule véritable différence entre une fonction
virtuelle pure et une virtuelle tout courte, c'est que la résolution
*dynamique* ne doit jamais rétrouver une fonction virtuelle pure ; c-à-d
qu'au niveau de l'implémentation, on n'en met jamais l'adresse dans une
vtbl. Mais on peut la définir, et si on l'appelle, il faut la définir.
(Note bien que vue qu'elle est virtuelle, et que la résolution dynamique
ne doit jamais la trouver, la seule façon de l'appeler, c'est d'en
qualifier le nom lors de l'appel.)

--
James Kanze GABI Software http://www.gabi-soft.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


Avatar
Jean-Marc Bourguet
writes:

Jean-Marc Bourguet wrote in message

[les membres virtuels purs] sont des membres virtuels comme les
autres a deux differences pres:
- les descendants instanciables sont obliges de les supplanter
- on peut ne pas les definir (a l'exception du destructeur)


D'où est-ce que tu tiens ça ?


J'ai l'impression que tu confonds "on peut ne pas" avec "on ne peut
pas".


En fait, à peu près la seule véritable différence entre une fonction
virtuelle pure et une virtuelle tout courte, c'est que la résolution
*dynamique* ne doit jamais rétrouver une fonction virtuelle pure ;



class A {
~A() { g(); }
virtual void f() = 0;
void g();
};

void A::g() {
f();
}
void A::f() {
std::cout << "In A::f(), a pure virtual functionn";
}

Ou bien tu ne consideres pas l'appel de f dans g comme etant une
resolution dynamique?

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
Gabriel Dos Reis
"K. Ahausse" writes:

| "Gabriel Dos Reis" a écrit dans le message de
| news:
| > "K. Ahausse" writes:
| >
| > | "Jean-Marc Bourguet" a écrit dans le message de
| > | news:
| > | > "K. Ahausse" writes:
| > | >
| > | >
| > | > Je ne vois pas ce que les virtuelles pures viennent faire ici.
| > |
| > | Oups, pardon. Je croyais qu'il était question de l'appel d'une fonction
| > | virtuelle pure (définie dans la classe dérivée) à partir du constructeur
| de
| > | la classe de base.
| > |
| > | Pour citer, James Kanze : "La seule chose à se rappeler, c'est que
| pendant
| > | l'execution d'un constructeur ou d'un destructeur de la class C, le type
| > | dynamique est C. On ne tient pas compte du futur (dans le cas d'un
| > | constructeur) ..."
| > |
| > | Et donc : a la construction de la classe de base, la fonction virtuelle
| > | pure n'étant pas encore définie, on ne peut faire appel à elle.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| >
| > Mais c'est aussi un non-sense :
| >
| > #include <iostream>
| >
| > struct A {
| > virtual void f() = 0;
| > virtual ~A() { f(); }
| > };
| >
| > void A::f() { std::cout << "coucou !n"; }
|
| Oui, je vois, tu as raison aussi :-)

Non. Je n'ai pas raison : la fonction est *définie*, mais le code
invoque un fonctionnement indéfini (la plupart des compilateurs
l'accepteront sur la base du méchanisme d'appel).

-- Gaby
Avatar
Jean-Marc Bourguet
Gabriel Dos Reis writes:

| > #include <iostream>
| >
| > struct A {
| > virtual void f() = 0;
| > virtual ~A() { f(); }
| > };
| >
| > void A::f() { std::cout << "coucou !n"; }
|
| Oui, je vois, tu as raison aussi :-)

Non. Je n'ai pas raison : la fonction est *définie*, mais le code
invoque un fonctionnement indéfini (la plupart des compilateurs
l'accepteront sur la base du méchanisme d'appel).


Evidemment ils ont ete mettre en 10.4/3 et une clause qui contredit ce
qu'on peut penser en ne regardant que 12.7 (et en particulier 12.7/3).

Je comprends que l'effet soint indefini quand la fonction n'est pas
definie, mais si elle l'est ca me choque un peu.

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
Jean-Marc Bourguet
(Note supersedes suite au message de Gaby)

writes:

Jean-Marc Bourguet wrote in message

[les membres virtuels purs] sont des membres virtuels comme les
autres a deux differences pres:
- les descendants instanciables sont obliges de les supplanter
- on peut ne pas les definir (a l'exception du destructeur)


D'où est-ce que tu tiens ça ?


J'ai l'impression que tu confonds "on peut ne pas" avec "on ne peut
pas".


En fait, à peu près la seule véritable différence entre une fonction
virtuelle pure et une virtuelle tout courte, c'est que la résolution
*dynamique* ne doit jamais rétrouver une fonction virtuelle pure ;


Je pensais que

class A {
~A() { g(); }
virtual void f() = 0;
void g();
};

void A::g() {
f();
}
void A::f() {
std::cout << "In A::f(), a pure virtual functionn";
}

etait conforme a la lecture de 12.7/3. Mais 10.4/6 dit que c'est indefini.

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
Jean-Marc Bourguet
(Supersedes, pour corriger une typo dans une reference)

Gabriel Dos Reis writes:

| > #include <iostream>
| >
| > struct A {
| > virtual void f() = 0;
| > virtual ~A() { f(); }
| > };
| >
| > void A::f() { std::cout << "coucou !n"; }
|
| Oui, je vois, tu as raison aussi :-)

Non. Je n'ai pas raison : la fonction est *définie*, mais le code
invoque un fonctionnement indéfini (la plupart des compilateurs
l'accepteront sur la base du méchanisme d'appel).


Evidemment ils ont ete mettre en 10.4/6 une clause qui contredit ce
qu'on peut penser en ne regardant que 12.7 (et en particulier 12.7/3).

Je comprends que l'effet soint indefini quand la fonction n'est pas
definie, mais si elle l'est ca me choque un peu.

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