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

initialisation des variables membres par défaut

73 réponses
Avatar
Richard Delorme
// ---------8<----------------------------
#include <iostream>

class A {
private:
int x;

public:
A() {};
~A() {};
void print() {
std::cout << x << std::endl;
}
};


int main() {
A t;
int y;


t.print();
std::cout << y << std::endl;

return 0;
}
// ---------8<----------------------------

Dans l'exemple ci-dessus, ni x, membre de la classe A, ni la variable
locale y ne sont initialisées.
gcc détecte le cas de la variable locale :
attention : ‘y’ may be used uninitialized in this function
mais reste silencieux sur la variable membre.
Valgrind signale les deux cas (de manière guère compréhensible d'ailleurs).

Personnellement je pensais comme Valgrind que la variable x devait être
initialisé explicitement dans le constructeur, par exemple en écrivant :
A() : x() {};
à la place du constructeur ci-dessus.

Néanmoins la lecture de blogs sur internet me laisse dans le doute. Par
exemple ici :
http://discuss.joelonsoftware.com/default.asp?joel.3.489111.50

On lit tout et son contraire. Qu'en est il en pratique ? (et en théorie
aussi d'ailleurs).

--
Richard

10 réponses

4 5 6 7 8
Avatar
Wykaaa
James Kanze a écrit :
On 28 Jan, 11:53, Wykaaa wrote:
Stan a écrit :
On 28 jan, 00:09, Wykaaa wrote:





[...]





Quel est le plus évolutif ?
Où est ta différence valeur/comportement ?







Même avec une conception objet du logiciel efficace,
à un moment donné, il te faudra bien implémenter
*un peu* d'algorithmie.





Et là, tu peux dire ce que tu veux, pour être efficace (en
rapidité, lisibilité et maintenance), les enum, switch ou
if/else sont tout adaptés. Lorsque c'est bien, conçu, ce ne
pose aucun pb ( à l'echelle de l'algo évidemment).





Mon exemple ne te parle-t-il pas ?



Et comment. Il montre bien le problème de ne pas utiliser un
enum. Utiliser des classes distinctes, comme tu as voulu faire,
signifie que les fonctions qui déterminent le jour doivent
connaître ton application.



Ah oui ? et pourquoi ?

Et même si cela était gênant, dans certain cas, j'utiliserais le pattern
Factory ou AbstractFactory.

Il va radicalement à l'encontre de
l'encapsulation. Tandis que l'enum...



Ben, il est pas encapsulé du tout...

(Et en passant, est-ce que tu penses réelement qu'il faut que le
code prenne en compte la possibilité que le nombre de jours dans
la semaine change ? Il y a quand même des limites à la
généricité.)



Mon exemple était là pour illustrer le principe. Il ne faut pas le
prendre "au pied de la lettre". Supposons qu'il s'agisse d'instructions
assembleur (au lieu des jours) et que travailler() soit la fonction
executer() et que le jeu d'instructions évolue.

Les switches sont une horreur pour la maintenance et les évolutions
quand on fait de l'objet alors que le polymorphisme rend les
"aiguillages" automatiques quand on ajoute des sous-classes.



Le polymorphisme est une horreur pour la maintenance quand
l'information décisive est calculée autre part que dans le code
applicatif.



C'est juste une question de documentation d'interface.
Avatar
Wykaaa
James Kanze a écrit :
On 27 Jan, 23:09, Wykaaa wrote:
James Kanze a écrit :



On Jan 27, 7:45 pm, Wykaaa wrote:
Marc Espie a écrit :


[...]
Les variables non initialisees (et leur copain, les switch
sur un champ type) sont des symptomes de design faits par
des gens qui n'ont pas encore bien integre la conception
objet (ou qui, pour de basses questions de temps, n'ont pas
termine leur design).









En approche objet, je n'utilise jamais des enums. Je passe
toujours par l'héritage.







Étant donné que les deux sert à des buts complètement
différents, je ne vois pas le rapport. J'en utilise les deux.





Non, il n'ont pas des buts différents.



Je regrette, mais ils n'ont aucun rapport immédiat. Il y a bien
queques cas où on pourrait se servir de l'un ou de l'autre, mais
ils sont assez rare. En général, au moins dans un langage comme
le C++ qui supporte les deux, c'est l'un ou l'autre qui
s'impose, selon le cas, et l'utilisation de l'autre donne un
programme nettement inférieur.

Un enum, c'est une classe abstraite et il y a autant de
sous-classes que de valeurs dans l'enum.







Sauf qu'un enum, ça n'a rien à voir avec une classe,
abstraite ou non. (Pour commencer, un enum, c'est une
valeur, et une classe abstraite suppose en général un
comportement et une identité. Tout à fait l'opposer d'un
enum.)





Tu veux dire qu'un enum est une variable scalaire quand tu dis
"valeur"?



Exactement. C'est une valeur, au sens absolut du terme.

Et un enum, ça a un comportement puisqu'on le gère avec des
switches...



Si tu veux faire des bêtises, effectivement, il n'y a rien dans
le langage qui te l'empêche. Mais des switch sur des enum, c'est
quand même des exceptions. Tout au plus, on fait un if sur une
valeur spéciale (genre OK), ou on se sert comme indice dans un
tableau (par exemple, pour récupérer le message à afficher).



La plupart des switches sur des enums qu'on trouve en C devrait se
traduire par de l'héritage et du polymorphisme en C++.
Quand on a des imbrications de switches dans des programmes C, ça
correspond, en général, à une vraie hiérarchie qu'on exprime par de
l'héritage et du polymorphisme en C++.

Mais l'enum sert surtout à transporter l'information entre des
couches de programmation qui ne se connaissent pas, d'une façon
simple et efficace. Il transporte l'information ; il n'a pas de
comportement.



`Si tu utilises les enums seulement pour faire du transport
d'information et que tu n'as aucun comportement attaché, alors je ne
suis pas contre ce genre d'utilisation, au contraire.

1) enum



enum Jour {LUNDI, MARDI, MERCREDI, ...};
Jour j = ...;



void travailler(Jour leJour)
{
switch(j)
{
case LUNDI : //faire ceci
case MARDI : //faire cela
case MERCREDI : //faire autre chose
...
}



travailler(j);
N'est-ce pas une fonction qui décrit un comportement de l'enum ?



2) polymorphisme



class Jour
{
public:
virtual void travailler()=0;
// éventuellement d'autres méthodes
}



class Lundi
{
public:
virtual void travailler() {//faire ceci}
};
class Mardi
{
public:
virtual void travailler() {//faire cela}
};
class Mercredi
{
public:
virtual void travailler() {//faire autre chose}
};



Jour j = new ...;



j.travailler();



Quel est le plus évolutif ?



Le premier, de loin. C'est un exemple type où l'enum convient,
et la classe polymorphique non. Parce que le code qui convertit
la date système en jour ne doit rien savoir de ton travailler,
même pas qu'il existe.



Le code qui convertit la date système en jour n'a pas à appeler la
fonction travailler(), ça je te l'accorde et même, si elle le fait,
c'est un défaut de dépendance (fort couplage !).

Ensuite, si tu as des dépendences dans le comportement, on
pourrait imaginer une hièrarchie qui les encapsule. Quelque
chose du genre :

class Activité
{
public:
virtual ~Activité();
virtual void travailler() = 0;
};

class ActivitéDeJourDeLaSemaine : public Activité ...
class ActivitéDeSamedi : public Activité ...
class ActivitéDeDimanche : public Activité ...

ActivitéFactory* dispatch[] > {
&activitéDeJourDeLaSemaineFactory,
&activitéDeJourDeLaSemaineFactory,
&activitéDeJourDeLaSemaineFactory,
&activitéDeJourDeLaSemaineFactory,
&activitéDeJourDeLaSemaineFactory,
&activitéDeSamediFactory,
&activitéDeDimancheFactory
};

std::auto_ptr< Activité >
getActivité( JourDeLaSemaine jour )
{
return dispatch[ jour ]->createInstance();
}



Cela est trop compliqué et non maintenable.
En plus tes classes "ActivitéDe..." sont orientées traitement et pas
objet. Aurais-tu fait du SART auparavant ?

Les "Activités" doivent forcément être des méthodes de classe et non des
classes encapsulées dans des objets qui représentent les entités du
domaine de l'application.

Évidemment, le jour qu'on change le nombre de jours dans la
semaine, il va falloir retoucher le tableau. Mais c'est une
risque acceptable, à mon avis. Et en fait, ça serait
probablement toujours moins de travail que ta solution, parce
qu'on peut supposer que le jour qu'on ajoute, c'est un jour de
semaine, et donc, la classe nécessaire est déjà écrite.

Dans l'ensemble, je crois que tu as bien mal choisi ton exemple.



Non, il est bien choisi sauf pour le cas où on va ajouter des
sous-classes. A moins qu'on change radicalement le calendrier en
inventant un nouveau jour comme jacadi :-) (Jacques est mon vrai prénom...).
Avatar
Richard Delorme
Le 27/01/2010 20:45, Wykaaa a écrit :
Marc Espie a écrit :



C'est l'effet de la programmation de "style C" en C++... et ça
correspond à ce que je dis dans un autre post de ce fil : programmer en
style C avec C++, c'est pire que de rester en C et on le voit avec éclat
ici.



Moi qui croyais que le C++ était un langage multiparadigme.

La classe Move est à revoir totalement et, je le répète,
"promotion" n'a rien à faire ici.



Si.

Les variables non initialisees (et leur copain, les switch sur un
champ type)
sont des symptomes de design faits par des gens qui n'ont pas encore bien
integre la conception objet (ou qui, pour de basses questions de temps,
n'ont pas termine leur design).



En approche objet, je n'utilise jamais des enums. Je passe toujours par
l'héritage.
Un enum, c'est une classe abstraite et il y a autant de sous-classes que
de valeurs dans l'enum.



Je dois avouer ne pas comprendre ce que vous voulez dire. En tout cas je
n'utilise pas spécialement les enums pour faire des switch. La fonction
de génération des coups de mon programme d'échecs ressemble à:
// Piece enumeration
enum Piece {
EMPTY = 0,
OUT,
WHITE_PAWN,
BLACK_PAWN,
WHITE_KNIGHT,
BLACK_KNIGHT,
//etc.
};

const Piece PAWN[] = {WHITE_PAWN, BLACK_PAWN};
const Piece KNIGHT[] = {WHITE_KNIGHT, BLACK_KNIGHT};
// etc.

enum Color {
WHITE = 0,
BLACK,
};

enum Coordinate {
NOWHERE = 0,
A1 = 21, B1, C1, D1, E1, F1, G1, H1,
A2 = 31, B2, C2, D2, E2, F2, G2, H2,
//etc.
};

// Check if the player in turn can move
void Board::generate_moves(MoveArray &move_array) const {
assert(is_valid());

const Color &p = player; //<- enum
const Color o = opponent(p); //<- enum
const PieceLocation *l;
Coordinate from, to; //<- enum
int pinning_direction, d;

move_array.clear();

switch(in_check) {
case 0: // pas enum

// pawn
foreach_piece_location(l, list[PAWN[p]]) {
from = l->x;
pinning_direction = get_pinning_direction(from, p, o);
if (is_on_promotion_rank(from, p)) {
generate_pawn_promotion_capture(move_array, from,
PAWN_DIRECTION[p][0], pinning_direction, o);
generate_pawn_promotion_capture(move_array, from,
PAWN_DIRECTION[p][1], pinning_direction, o);
generate_pawn_promotion(move_array, from,
PUSH_DIRECTION[p], pinning_direction);
} else {
generate_pawn_capture(move_array, from,
PAWN_DIRECTION[p][0], pinning_direction, o);
generate_pawn_capture(move_array, from,
PAWN_DIRECTION[p][1], pinning_direction, o);
generate_pawn_move(move_array, from,
PUSH_DIRECTION[p], pinning_direction);
}
}

// en passant capture
if (enpassant != NOWHERE) {
assert(square[enpassant] == PAWN[o]);
to = enpassant + PUSH_DIRECTION[p];
generate_pawn_enpassant_capture(move_array, enpassant - 1, to);
generate_pawn_enpassant_capture(move_array, enpassant + 1, to);
}

// knight move
foreach_piece_location(l, list[KNIGHT[p]]) {
from = l->x;
if (get_pinning_direction(from, p, o) == 0) {
generate_knight_move(move_array, from, KNIGHT_DIRECTION[0], o);
generate_knight_move(move_array, from, KNIGHT_DIRECTION[1], o);

//etc.


Il y a effectivement un switch, mais il se fait sur une valeur entière,
et non un enum, "in_check" qui indique le nombre de pièces causant un
échec (0: pas d'échec, 1: échec simple, 2: échec double). Quant à mes
enum, il me servent de valeurs contenues dans des tableaux (échiquier,
listes de pièces), des variables ou passées à des fonctions. Ils sont
juste là pour rendre le code plus lisibles que la simple utilisation de
valeurs numériques. De plus, comme le typage C++ des enums est fort, ça
évite de prendre une couleur pour une pièce, ce que ne permet pas le C.
Ça permet de détecter des bêtise à la compilation. AMHA, on a plus
intérêt à utiliser des enums en C++ qu'en C, où ils n'apportent pas
grand chose de plus que des constantes, voire des macros.

--
Richard


--
Richard
Avatar
Wykaaa
Richard Delorme a écrit :
Le 27/01/2010 20:45, Wykaaa a écrit :
Marc Espie a écrit :



C'est l'effet de la programmation de "style C" en C++... et ça
correspond à ce que je dis dans un autre post de ce fil : programmer en
style C avec C++, c'est pire que de rester en C et on le voit avec éclat
ici.



Moi qui croyais que le C++ était un langage multiparadigme.



Oui, c'est vrai mais ça dépend comment on utilise les différents
paradigmes au sein de l'application.
Si on utilise C++ juste comme du C amélioré, par rapport à mon
expérience, je suis convaincu qu'il vaut mieux rester en C.

La classe Move est à revoir totalement et, je le répète,
"promotion" n'a rien à faire ici.



Si.



Bon, il faudrait une discussion approfondie sur le sujet qui devrait se
dérouler dans fr.comp.objet mais pour cela il faudrait disposer de
l'ensemble de la conception...

Les variables non initialisees (et leur copain, les switch sur un
champ type)
sont des symptomes de design faits par des gens qui n'ont pas encore
bien
integre la conception objet (ou qui, pour de basses questions de temps,
n'ont pas termine leur design).



En approche objet, je n'utilise jamais des enums. Je passe toujours par
l'héritage.
Un enum, c'est une classe abstraite et il y a autant de sous-classes que
de valeurs dans l'enum.



Je dois avouer ne pas comprendre ce que vous voulez dire. En tout cas je
n'utilise pas spécialement les enums pour faire des switch.



Ok. Vous ne les utilisez pas pour décrire des entités qui ont un
comportement.


[couic le code : excusez-moi]



Il y a effectivement un switch, mais il se fait sur une valeur entière,
et non un enum, "in_check" qui indique le nombre de pièces causant un
échec (0: pas d'échec, 1: échec simple, 2: échec double). Quant à mes
enum, il me servent de valeurs contenues dans des tableaux (échiquier,
listes de pièces), des variables ou passées à des fonctions. Ils sont
juste là pour rendre le code plus lisibles que la simple utilisation de
valeurs numériques. De plus, comme le typage C++ des enums est fort, ça
évite de prendre une couleur pour une pièce, ce que ne permet pas le C.
Ça permet de détecter des bêtise à la compilation. AMHA, on a plus
intérêt à utiliser des enums en C++ qu'en C, où ils n'apportent pas
grand chose de plus que des constantes, voire des macros.



Cette utilisation ne me choque pas du tout et vous avez même tout à fait
raison de mentionner qu'ils sont de bien meilleure compagnie en C+ qu'en
C pour l'usage que vous en faites.

Ma remarque initiale sur les enum et les switches partait de la remarque
de Marc Espie, je cite :
"Les variables non initialisees (et leur copain, les switch sur un champ
type) sont des symptomes de design faits par des gens qui n'ont pas
encore bien integre la conception objet (ou qui, pour de basses
questions de temps, n'ont pas termine leur design)."

qui introduisait les switches.

Si on utilise les enum juste pour augmenter la lisibilité et le typage
par rapport aux entiers, je n'ai rien contre mais il ne faut pas leur
associer de comportement car, dans ce cas, il faut utiliser les classes,
l'héritage et le polymorphisme. C'était juste le sens de mes remarques
que je n'ai peut-être pas suffisamment explicité.
Avatar
Mickaël Wolff
Stan a écrit :
Or quand dans une AbstractFactory , pour créer tes objets tu n'as
pas 10000 possibilités ;
tu peux utiliser un tableau associatif, ou bien un selecteur de type
if/else,
ou encore un switch + enum.
Pour des construction reposant sur un int, ce dernier choix est tout à
fait
acceptable.



Ce ne serait pas mieux d'utiliser la surcharge de fonction dans ce
cas là ? Par exemple :

struct factory
{
void create(machin * truc) ;
void create(autre_machin * truc) ;
} ;

--
Mickaël Wolff aka Lupus Michaelis
http://lupusmic.org
Avatar
Mickaël Wolff
Wykaaa a écrit :

Non, il est bien choisi sauf pour le cas où on va ajouter des
sous-classes. A moins qu'on change radicalement le calendrier en
inventant un nouveau jour comme jacadi :-) (Jacques est mon vrai
prénom...).



Une révolution est pourtant vite arrivée, et un calendrier remplacé ;)

--
Mickaël Wolff aka Lupus Michaelis
http://lupusmic.org
Avatar
Stan
On 29 jan, 05:59, Mickaël Wolff wrote:
Stan a écrit :

> Or quand dans une AbstractFactory , pour créer tes objets tu n'as
> pas 10000 possibilités ;
> tu peux utiliser un tableau associatif, ou bien un selecteur de type
> if/else,
> ou encore un switch + enum.
> Pour des construction reposant sur un int, ce dernier choix est tout à
> fait
> acceptable.

Ce ne serait pas mieux d'utiliser la surcharge de fonction dans ce
cas là ? Par exemple :

struct factory
{
void create(machin * truc) ;
void create(autre_machin * truc) ;

} ;




Prenons un cas concret: interprétation d'un protocole applicatif
'lambda'.
Dans une trame, tu as un champ type :
- 0x1 : message d'état
- 0x2 : message de supervison
- 0x3 : alarme(s) systeme
- 0x4 : etc.

la suite du champ type contient les paramètres.

Comment utilises-tu ton exemple ?

--
-Stan
Avatar
James Kanze
On Jan 28, 8:39 pm, Wykaaa wrote:
James Kanze a crit :
> On 27 Jan, 22:26, Wykaaa wrote:
>> Stan a écrit :



>>> On 27 jan, 20:45, Wykaaa wrote:



>>>> En approche objet, je n'utilise jamais des enums. Je
>>>> passe toujours par l'h ritage. Un enum, c'est une classe
>>>> abstraite et il y a autant de sous-classes que de valeurs
>>>> dans l'enum.



>>> Faut pas faire dans le dogmatisme. Il faut utiliser les
>>> enums pour ce quoi elle sont utiles; effectivement, si
>>> c'est pour faire un m canisme de s lection de type, le
>>> polymorphisme est plus adapt . Mais dans ce cas, tu
>>> aurais en dire autant des switches.



>> La chasse aux switches est la programmation objet ce que la
>> chasse aux gotos est la programmation structurée.



> Pas tout fait : si c'est vrai qu'on en a beaucoup moins
> besoin des switches quand on a le polymorphisme, il y a
> encore des cas, par exemple, un switch sur un caract re lu
> sur l'interface utilisateur. (Une fois, juste pour voir,
> j'ai essay d'écrire un bout de code sans les switch. Ça ne
> tient pas la route quand on a affaire des entrées en
> provenance de l'exterieur. Ni, par exemple, pour des choses
> du genre code d'erreur.)



Pour les 2 cas que tu cites,je suis d'accord. Dans un
analyseur lexical par exemple, les switches sont inévitables
puisqu'on ne connait pas le type de la donnée d'entrée.



Pour certains analyseurs lexicaux, s'entend. Les switch sont
commodes pour les analyseurs simples ; au delà d'un certain
point, je passe aux expressions rationnelles (mais j'ai des
switch dans mon parseur d'expressions rationnelles).

Pour les codes d'erreur, il est préférable d'utiliser les
mécanismes d'exception quand on peut



Ça dépend du type d'erreur. Il y a encore bien des cas où le
code de retour est préférable. Et l'utilisation des exceptions
n'exclut pas non plus l'enum ; il s'agit toujours de transporter
une information (succincte), dont on ne sait pas comment
l'utilisateur final s'en servira.

mais a peut-être pénalisant pour les performances dans le cas
où l'algorithme impose une programmation par exception comme
pour un mécanisme de gestion des entrées/sorties dans un noyau
de système d'exploitation.



C'est vraiment rarissime de devoir écarter les exceptions à
cause des performances. (Dans le noyau d'un système
d'exploitation, évidemment, il y a beaucoup de choses qui ne
vont pas -- c'est difficile à se servir de l'opérator new, par
exemple, si tu es en train d'implémenter la gestion de la
mémoire du SE.)

>> Les enums conduisent in vitablement des switches, c'est
>> pour cela que je ne les utilise pas.



> Tiens, là, tu m'apprends quelques choses. Parce que j'ai bien
> des enum dans mon code, mais pas de switch sur l'enum. (À
> quelques exceptions près.)



Ca veut dire que tu ne les utilises pas comme des ensembles de
valeur traiter alors ?



Certes. De la même façon que les int sont des ensembles à
traiter. Dans l'ensemble, en revance, c'est assez rare que je
les utilise pour des ensembles dont chaque élément a un
comportement distinct.

[...]
>> On n'aura tester que le code qui correspond aux nouvelles
>> valeurs.



> Pas du tout. Jamais une modification sans faire passer le jeu
> complet des tests de regression.



Je ne suis pas d'accord. Il y a des cas où on peut se
dispenser de passer la totalit des tests de non régression en
toute sécurité.



Le système de build chez moi ne le permet pas. C'est impossible,
tel que j'ai écrit mes fichiers de make, d'exporter un en-tête
ou des fichiers objet sans que tous les tests passent.

C'est un des b a ba de la qualité.

Heureusement, dans un compilateur par exemple, qu'on ne va pas
passer tous les tests de non régression la moindre
modification du code...



Est-ce que ça ne serait-il pas l'explication des problèmes de
qualité dans certains compilateurs ?

[...]
> Tout fait. Et comme mal écrit , on entend aussi les cas où
> la technologie ne convenait pas.



Il y a effectivement des applications pour lesquelles l'objet
ne convient pas.



Voire même des parties de l'application où l'objet ne convient
pas. Qu'est-ce que l'objet apporte dans la représentation d'un
adresse IP, ou un montant monétaire ?

> [...]
> Par définition, n'est-ce pas ? C'est pour a qu'il faut bien
> dire que l'objet n'est pas un silver bullet ; qu'il ne convient
> pas pour tout. (Je fais pas mal de calcul numérique en ce
> moment ; heureusement que je ne suis pas obligé à me servir
> d'une classe polymorique pour les doubles.)



Ca dépend du contexte. J'ai connu une application qui faisait
des calculs de déformation de pièces en fonction de
contraintes tangentielles pour fabriquer des abaques
d'usinage. les polynômes manipulés pouvaient avoir des
coefficients de toute nature (rationnels, réels et même des
matrices). On a utilisé le polymorphisme pour les coefficients
et cela a rendu le code d'une simplicit biblique alors que le
problème était très complexe.



Comme tu dis, ça dépend. Le désavantage des objets
polymorphiques, c'est qu'on ne peut pas facilement les copier ;
on se retrouve avec une sémantique de référence. Que ça convient
ou non.

[...]
Je pense que c'est parce que, aujourd'hui, on utilise le C++
là où on devrait continuer utiliser C.



Il n'y a nul part où on devrait continuer à utiliser le C. Les
contrôles d'accès apporte déjà suffisamment de justifier le C++,
même si on ne se sert de rien d'autre en plus du C.

[...]
>> mais quand on est dans l'industrie, ça devient un problème
>> d'éthique : on ne fait pas joujou avec le code quand il s'agit
>> de logiciels pour l'avionique ou les centrales nucl aires. En
>> plus,il y a des m canismes de certification draconiens.



> Qui, la plupart du temps, interdisent le polymorphisme.



Et bien justement, pas toujours et on peut faire du code absolument sûr
en utilisant intensivement le polymorphisme



Mais c'est quand même plus difficile à prouver que le code
marche quand on ne sait pas quelle fonction sera appelée.

--
James Kanze
Avatar
James Kanze
On Jan 28, 9:34 pm, Wykaaa wrote:
James Kanze a écrit :
> On 28 Jan, 11:53, Wykaaa wrote:
>> Stan a écrit :
>>> On 28 jan, 00:09, Wykaaa wrote:



>>> [...]
> Et comment. Il montre bien le problème de ne pas utiliser un
> enum. Utiliser des classes distinctes, comme tu as voulu faire,
> signifie que les fonctions qui déterminent le jour doivent
> connaître ton application.



Ah oui ? et pourquoi ?



Comment pourrait-il instancier une classe propre à ton
application, s'il ne connaît pas ton application.

Et même si cela était gênant, dans certain cas, j'utiliserais
le pattern Factory ou AbstractFactory.



Ce qui ne change rien, parce qu'il faudrait que la module qui
utilise le Factory le connaissent.

Le problème de base, c'est que le jour de la semaine, c'est une
information, pûr et simple, et qu'il n'a pas, et ne doit pas
avoir, de comportement. Le comportement qui dépend du jour de la
semaine, c'est propre à ton application, et indépendant du
concepte « jour de la semaine ». Et que les parties du programme
qui manipule des jours de la semaine ne doivent pas avoir
connaissance de ce comportement. Dans les modules qui sont
concernées par ce comportement, on pourrait bien être amener à
créer des classes, peut-être au moyen des factorys qu'on
retrouve en indiciant dans un tableau au moyen de l'enum. Mais
on ne va pas s'amuser à charrier des objets polymorphiques à
droit et à gauche à travers des modules qui n'ont rien à foutre.

> Il va radicalement à l'encontre de l'encapsulation. Tandis
> que l'enum...



Ben, il est pas encapsulé du tout...



Pas autant qu'on voudrait. Mais ce n'est pas la question : il
contient une information qu'on veut exporter, qu'on veut
transporter. Ce qu'on veut éviter, c'est que localtime (dans la
bibliothèque C) ait besoin de connaître tes classes.

> (Et en passant, est-ce que tu penses réelement qu'il faut
> que le code prenne en compte la possibilité que le nombre de
> jours dans la semaine change ? Il y a quand même des limites
> à la généricité.)



Mon exemple était là pour illustrer le principe. Il ne faut
pas le prendre "au pied de la lettre". Supposons qu'il
s'agisse d'instructions assembleur (au lieu des jours) et que
travailler() soit la fonction executer() et que le jeu
d'instructions évolue.



C'est déjà un autre problème, parce qu'une instruction
assembleur a une sémantique bien définie. Elle n'est pas
simplement une « information », dans le sens que le jour de la
semaine est une information, et rien de plus.

>> Les switches sont une horreur pour la maintenance et les
>> évolutions quand on fait de l'objet alors que le
>> polymorphisme rend les "aiguillages" automatiques quand on
>> ajoute des sous-classes.



> Le polymorphisme est une horreur pour la maintenance quand
> l'information décisive est calculée autre part que dans le
> code applicatif.



C'est juste une question de documentation d'interface.



En somme, tu es d'accord avec moi : qu'il manque de
l'encapsulation. Parce que je ne vois pas pourquoi l'auteur de
localtime (chez Microsoft ou Sun) doit avoir besoin de lire la
documentation de mes classes.

--
James Kanze
Avatar
Wykaaa
James Kanze a écrit :
On Jan 28, 8:39 pm, Wykaaa wrote:




[snip]

Je ne suis pas d'accord. Il y a des cas où on peut se
dispenser de passer la totalit des tests de non régression en
toute sécurité.



Le système de build chez moi ne le permet pas. C'est impossible,
tel que j'ai écrit mes fichiers de make, d'exporter un en-tête
ou des fichiers objet sans que tous les tests passent.

C'est un des b a ba de la qualité.



Ce n'est pas à moi que tu vas dire cela. J'ai été responsable qualité de
nombreuses années.

Heureusement, dans un compilateur par exemple, qu'on ne va pas
passer tous les tests de non régression la moindre
modification du code...



Est-ce que ça ne serait-il pas l'explication des problèmes de
qualité dans certains compilateurs ?



Non. J'ai fait des compilateurs pendant 10 ans. On avait fabriqué tout
un système qui permettait de ne passer que les tests nécessaires en
fonction des modifications effectuées dans la grammaire, par exemple
(pour les analyseurs lexicaux et syntaxiques).

Evidemment, lors de modifications dans l'optimiseur on repassait tout
car on n'est ps à l'abri d'une divergence (un algo d'optimisation défait
ce que le précédent a fait, etc.)

[...]
Tout fait. Et comme mal écrit , on entend aussi les cas où
la technologie ne convenait pas.





Il y a effectivement des applications pour lesquelles l'objet
ne convient pas.



Voire même des parties de l'application où l'objet ne convient
pas. Qu'est-ce que l'objet apporte dans la représentation d'un
adresse IP, ou un montant monétaire ?



Pour la monnaie, si on manipule plusieurs devises et des conversions,
c'est utile (des factures dans les différentes filiales d'une entreprise
multinationale, par exemple).

[...]
Par définition, n'est-ce pas ? C'est pour a qu'il faut bien
dire que l'objet n'est pas un silver bullet ; qu'il ne convient
pas pour tout. (Je fais pas mal de calcul numérique en ce
moment ; heureusement que je ne suis pas obligé à me servir
d'une classe polymorique pour les doubles.)





Ca dépend du contexte. J'ai connu une application qui faisait
des calculs de déformation de pièces en fonction de
contraintes tangentielles pour fabriquer des abaques
d'usinage. les polynômes manipulés pouvaient avoir des
coefficients de toute nature (rationnels, réels et même des
matrices). On a utilisé le polymorphisme pour les coefficients
et cela a rendu le code d'une simplicit biblique alors que le
problème était très complexe.



Comme tu dis, ça dépend. Le désavantage des objets
polymorphiques, c'est qu'on ne peut pas facilement les copier ;
on se retrouve avec une sémantique de référence. Que ça convient
ou non.



Pour moi ceci est un faux débat et c'est même un alibi pour ceux qui ne
comprennent pas tout de C++ ou de l'objet en général.
Il y a des règles à appliquer quand on utilise l'héritage et le
polymorphisme. En particulier, il faut se poser la question de la
surcharge de l'opérateur d'assignation, du constructeur de copie, de
l'allocation dynamique dans les constructeurs, etc. Quelqu'un qui ne
maîtrise pas tout ça va forcément hésiter à se lancer dans du
polymorphisme intensif...

[...]
Je pense que c'est parce que, aujourd'hui, on utilise le C++
là où on devrait continuer utiliser C.



Il n'y a nul part où on devrait continuer à utiliser le C. Les
contrôles d'accès apporte déjà suffisamment de justifier le C++,
même si on ne se sert de rien d'autre en plus du C.



Les gens font ce qu'ils veulent mais utiliser C++ par rapport à C juste
pour les contrôles d'accès, c'est vraiment prendre un canon pour tuer
une mouche car on peut faire de la bonne encapsulation en C sans tout ceci.

[...]
mais quand on est dans l'industrie, ça devient un problème
d'éthique : on ne fait pas joujou avec le code quand il s'agit
de logiciels pour l'avionique ou les centrales nucl aires. En
plus,il y a des m canismes de certification draconiens.







Qui, la plupart du temps, interdisent le polymorphisme.





Et bien justement, pas toujours et on peut faire du code absolument sûr
en utilisant intensivement le polymorphisme



Mais c'est quand même plus difficile à prouver que le code
marche quand on ne sait pas quelle fonction sera appelée.



Il y a des méthodes et des outils pour faire ça.
4 5 6 7 8