OVH Cloud OVH Cloud

constructeur et derivation

9 réponses
Avatar
Laurent Deniau
C++ et d'autres langages OO n'imposent pas aux classes concretes de
redefinir les constructeurs herites meme si celles-ci definissent de
nouvelles donnees membres. Hors un oubli est facile lorsque la
hierarchie est consequente ou qu'un constructeur est rajoute dans une
base. Par exemple:

-- fichier AB.hpp (sans les balises)
struct A {
A();
int a;
};
struct B : A {
int b;
};

-- fichier AB.cpp
#include "AB.hpp"

A::A() {}

-- fichier main.c
#include <iostream>
#include "AB.hpp"

int main() {
B b;
std::cout << "b.(a,b) = (" << b.a << ',' << b.b << ")\n";
}

Est du C++ legal et le compilateur (du moins gcc 4.0.1 -std=c++98 -Wall
-W -pedantic) ne detecte pas l'utilisation invalide des membres a et b.
Il devrait cependant etre possible au compilateur de detecter l'absence
d'initialisation du membre a dans A::A() et l'absence de redefinition ou
de nouveau constructeur dans B alors qu'un nouveau membre b a ete ajoute.

Il me semble que la derivation abstraite<-concrete ou concrete<-concrete
implique tres frequement l'ajout de donnees membres. Il faudrait donc au
minimum soit surcharger les constructeurs herites soit ajouter de
nouveaux constructeurs (soit les deux) dans de tels heritages.

D'ou mes questions:

1- Connaissez-vous des design patterns ou la derivation
abstraite<-concrete ou concrete<-concrete n'implique pas l'ajout de
donnees membres?

2- Existe-t-il des design patterns ou il serait contraignant, voir
problematique, d'imposer la (re)definition de *tous* les constructeurs
public d'une classe des que celle-ci est concrete? Si vous avez des
references sur ce sujet, je suis biensur preneur. Merci.

a+, ld.


FU to fclc++

9 réponses

Avatar
Jean-Marc Bourguet
Laurent Deniau writes:

Est du C++ legal et le compilateur (du moins gcc 4.0.1 -std=c++98
-Wall -W -pedantic) ne detecte pas l'utilisation invalide des
membres a et b.


Mais ce probleme n'est du qu'a la particularite des types de bases
d'avoir un constructeur par defaut qui ne fait rien. Pour les autres
types, ou bien le constructeur par defaut est appele ou bien il faut
un appel explicite a un autre constructeur.

1- Connaissez-vous des design patterns ou la derivation abstraite<-concrete
ou concrete<-concrete n'implique pas l'ajout de donnees membres?


Les visiteurs et autres objets a nature fonctionnelle n'ont parfois
pas besoin de membres de type donnee.

2- Existe-t-il des design patterns ou il serait contraignant, voir
problematique, d'imposer la (re)definition de *tous* les
constructeurs public d'une classe des que celle-ci est concrete? Si
vous avez des references sur ce sujet, je suis biensur
preneur. Merci.


Il y a naturellement la question de comment initialiser les membres de
la classe derivee si on n'a pas les parametres qu'il faut.

Un autre type de problemes que cela poserait, je vois mal un
std::fstream qui aurait un constructeur prenant un std::streambuf*.

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
Laurent Deniau
Jean-Marc Bourguet wrote:
Laurent Deniau writes:


Est du C++ legal et le compilateur (du moins gcc 4.0.1 -std=c++98
-Wall -W -pedantic) ne detecte pas l'utilisation invalide des
membres a et b.



Mais ce probleme n'est du qu'a la particularite des types de bases
d'avoir un constructeur par defaut qui ne fait rien.


Cela n'empeche pas un diagnostic. Si par exemple le constructeur est
inline, gcc detecte le probleme (sous une autre forme).

Pour les autres
types, ou bien le constructeur par defaut est appele ou bien il faut
un appel explicite a un autre constructeur.


Pareil, un diagnostic reste possible. Meme si A initialisait a, le b de
B resterait non initialise ce qui amene rapidement a du code invalide.

1- Connaissez-vous des design patterns ou la derivation abstraite<-concrete
ou concrete<-concrete n'implique pas l'ajout de donnees membres?



Les visiteurs et autres objets a nature fonctionnelle n'ont parfois
pas besoin de membres de type donnee.


Pour moi un visiteur a soit un etat, soit un effet de bord, et mon
impression est que le reste est tres marginal. Les deux exemples
classiques que j'ai en tete sont le cumul et la persistance/affichage
d'un conteneur et dans les deux cas il doit avoir des donnees membres.
On peut biensur simplifier en prenant std::cout comme stream ou une
variable globale comme resultat mais j'ai l'impression que ca reste
marginal et qu'imposer la (re)definition des constructeurs n'est pas un
handicap.

Est-ce que tu as un exemple plus concret pour m'eclairer ma lanterne?

2- Existe-t-il des design patterns ou il serait contraignant, voir
problematique, d'imposer la (re)definition de *tous* les
constructeurs public d'une classe des que celle-ci est concrete? Si
vous avez des references sur ce sujet, je suis biensur
preneur. Merci.


Il y a naturellement la question de comment initialiser les membres de
la classe derivee si on n'a pas les parametres qu'il faut.


Je ne comprends pas ce point. Je n'ai pas dis que tu ne peux pas definir
de nouveaux constructeurs ou 'privatiser' des constructeurs herites...
As-tu un exemple precis?

Un autre type de problemes que cela poserait, je vois mal un
std::fstream qui aurait un constructeur prenant un std::streambuf*.


Il suffirait de le redeclarer private et le compilateur serait content.

a+, ld.


Avatar
Jean-Marc Bourguet
Laurent Deniau writes:

Jean-Marc Bourguet wrote:
Laurent Deniau writes:

Est du C++ legal et le compilateur (du moins gcc 4.0.1 -std=c++98
-Wall -W -pedantic) ne detecte pas l'utilisation invalide des
membres a et b.
Mais ce probleme n'est du qu'a la particularite des types de bases

d'avoir un constructeur par defaut qui ne fait rien.


Cela n'empeche pas un diagnostic. Si par exemple le constructeur est
inline, gcc detecte le probleme (sous une autre forme).

Pour les autres types, ou bien le constructeur par defaut est
appele ou bien il faut un appel explicite a un autre constructeur.


Pareil, un diagnostic reste possible. Meme si A initialisait a, le b
de B resterait non initialise ce qui amene rapidement a du code
invalide.


Oui, un diagnostic est possible et rien n'empeche les compilateurs de
l'emettre. C'est meme une bonne idee (a part que ce genre de
diagnostic a tendance a n'etre disponible qu'avec l'optimisation a
cause de l'analyse de flow necessaire pour ne pas avoir trop de faux
negatifs). Ce qui etait implicite dans mon message est la raison
historique de compatibilite avec le C et le soucis de ne pas imposer
sur des choses equivalentes a ce qui est faisable avec le C une
penalite en temps d'execution.

1- Connaissez-vous des design patterns ou la derivation abstraite<-concrete
ou concrete<-concrete n'implique pas l'ajout de donnees membres?
Les visiteurs et autres objets a nature fonctionnelle n'ont parfois

pas besoin de membres de type donnee.


Pour moi un visiteur a soit un etat, soit un effet de bord, et mon
impression est que le reste est tres marginal. Les deux exemples
classiques que j'ai en tete sont le cumul et la
persistance/affichage d'un conteneur et dans les deux cas il doit
avoir des donnees membres. On peut biensur simplifier en prenant
std::cout comme stream ou une variable globale comme resultat mais
j'ai l'impression que ca reste marginal et qu'imposer la
(re)definition des constructeurs n'est pas un handicap.

Est-ce que tu as un exemple plus concret pour m'eclairer ma
lanterne?


J'ai un exemple tres concret (mais avec des observeurs plutot que des
visiteurs) ou j'ai ecrit dix classes concretes heritant de classes
abstraites et sur ces dix classes, huit n'ont pas de membres donnee.
Expliquer le contexte et le pourquoi est un peu complique d'une part,
et de l'autre rentrer dans ces details sur un forum public ne me
semble pas une bonne idee.

2- Existe-t-il des design patterns ou il serait contraignant, voir
problematique, d'imposer la (re)definition de *tous* les
constructeurs public d'une classe des que celle-ci est concrete? Si
vous avez des references sur ce sujet, je suis biensur
preneur. Merci.
Il y a naturellement la question de comment initialiser les membres de

la classe derivee si on n'a pas les parametres qu'il faut.


Je ne comprends pas ce point. Je n'ai pas dis que tu ne peux pas
definir de nouveaux constructeurs ou 'privatiser' des constructeurs
herites... As-tu un exemple precis?


Si je te suis, tu veux forcer dans un certain nombre de cas l'ecriture
d'une serie de constructeurs prives et non implementes. Je ne vois
pas l'interet de le faire.

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
Laurent Deniau
Jean-Marc Bourguet wrote:
Si je te suis, tu veux forcer dans un certain nombre de cas l'ecriture
d'une serie de constructeurs prives et non implementes. Je ne vois


Une redeclaration suffira s'il devient prive.

pas l'interet de le faire.


A la reflection, moi non plus pour le C++. J'etais entrain de reflechir
dans un autre langage...

a+, ld.

Avatar
Loïc Joly
Laurent Deniau writes:


Jean-Marc Bourguet wrote:

Pour les autres types, ou bien le constructeur par defaut est
appele ou bien il faut un appel explicite a un autre constructeur.


Pareil, un diagnostic reste possible. Meme si A initialisait a, le b
de B resterait non initialise ce qui amene rapidement a du code
invalide.



Oui, un diagnostic est possible et rien n'empeche les compilateurs de
l'emettre. C'est meme une bonne idee (a part que ce genre de
diagnostic a tendance a n'etre disponible qu'avec l'optimisation a
cause de l'analyse de flow necessaire pour ne pas avoir trop de faux
negatifs). Ce qui etait implicite dans mon message est la raison
historique de compatibilite avec le C et le soucis de ne pas imposer
sur des choses equivalentes a ce qui est faisable avec le C une
penalite en temps d'execution.


Je suis assez d'accord. Le problème est la non initialisation des types
de base. Si j'ai :

class Personne
{
public:
string nom;
};

Je n'ai pas envie que le compilateur ma fasse une erreu, et je n'ai pas
envie de devoir me taper l'écriture d'un constructeur qui ne servirait à
rien.

Plus te temps passe, plus je me demande si doubler les types de base par
des équivalents initialisés par défaut ne serait pas une bonne idée
(ainsi, ceux qui ne veulent pas l'initialisation pour des raisons de
base n'en ont pas, les autres en ont).

--
Loïc



Avatar
Fabien LE LEZ
On Fri, 19 Aug 2005 12:29:54 +0200, Laurent Deniau
:

struct A {
A();
int a;
};

A::A() {}


J'ai du mal à comprendre le problème lié à la dérivation. Le code
ci-dessus ne pose-t-il pas déjà le même problème, à savoir, la
non-initialisation de A::a ?


En passant, j'aime beaucoup la possibilité d'initialisation qu'offre
PHP :

class A
{
var $a= 42;
};

Avatar
Laurent Deniau
Fabien LE LEZ wrote:
On Fri, 19 Aug 2005 12:29:54 +0200, Laurent Deniau
:


struct A {
A();
int a;
};

A::A() {}



J'ai du mal à comprendre le problème lié à la dérivation. Le code
ci-dessus ne pose-t-il pas déjà le même problème, à savoir, la
non-initialisation de A::a ?


Si biensur, mais je voulais montrer les deux cas en meme temps: oubli
simple et oubli dans la classe derivee.

a+, ld.


Avatar
Laurent Deniau
Jean-Marc Bourguet wrote:
Laurent Deniau writes:


Est du C++ legal et le compilateur (du moins gcc 4.0.1 -std=c++98
-Wall -W -pedantic) ne detecte pas l'utilisation invalide des
membres a et b.



Mais ce probleme n'est du qu'a la particularite des types de bases
d'avoir un constructeur par defaut qui ne fait rien.


C'est effectivement *tres* problematique. Je viens de passer une heure a
chercher pourquoi un code marchait de temps en temps et j'avais qqchose
comme:

for(int i; i<cnt; i++) {
// ...
}

perdu dans mon code. Meme avec g++ -std=c++98 -Wall -W -pedantic le
compilo ne bronche pas... GCC devrait avoir un flags pour emettre un
diagnostic pour les types de base.

a+, ld.


Avatar
Matthieu Moy
Laurent Deniau writes:

C'est effectivement *tres* problematique. Je viens de passer une heure
a chercher pourquoi un code marchait de temps en temps et j'avais
qqchose comme:

for(int i; i<cnt; i++) {
// ...
}

perdu dans mon code. Meme avec g++ -std=c++98 -Wall -W -pedantic le
compilo ne bronche pas... GCC devrait avoir un flags pour emettre un
diagnostic pour les types de base.


Ça n'est pas aussi bien qu'une verification statique, mais à priori,
valgrind t'aurait donné une erreur à l'éxécution.

--
Matthieu