OVH Cloud OVH Cloud

Constructor et vector par defaut

42 réponses
Avatar
korchkidu
Bonjour,

j'ai une classe dont les objets peuvent etre construits en specifiant ou
non le dernier parametre qui est un std::vector. J'avais donc simplement
utilise un parametre par defaut qui mettait le vector vide s'il n'etait
pas initialise. Mais dernierement, on me dit (avec des explications
fumeuses que je n'ai pas comprises...) qu'il fallait pas faire ca. Je me
retrouve donc a faire 2 constructeurs appelant une fonction init. Celui
qui n'a pas le parametre associe au vector le cree simplement (vide) et
appelle la fonction init. Je trouve ca plus que douteux comme solution.
Alors pourquoi la premiere solution n'etait pas correcte?

Merci d'avance,
K.

10 réponses

1 2 3 4 5
Avatar
Marc Boyer
In article <41dad088$, korchkidu wrote:
Marc Boyer wrote:
In article , Jean-Marc Bourguet wrote:

C'est à dire qu'on ne peut pas faire:
void foo(int i, double d=0);
typedef (*fun_i)(int);
typedef (*fun_i_d)(int,double);

fun_i fi= foo;
fun_i_di fid= foo;


Donc en restant dans le cas des objets, ca devrait marcher si je
comprends bien...


Que viennent faire les objets ici ?

Marc Boyer
--
Je ne respecte plus le code de la route à vélo depuis une double fracture
due au fait que j'étais le seul à le respecter.


Avatar
drkm
korchkidu writes:

drkm wrote:

Prendre l'adresse d'un objet passé par référence constante (et le
stocker pour après la fin de la fonction), c'est vicieux. Et si ce
paramètre a de plus une valeur par défaut ...


Mais ce n'est pas le cas ici. On a un vector de pointeurs qui est itere
pour creer nos propres donnees. De plus, on me parle de bug, de plantage ...


Dans ce cas, il n'y a pas de problème. Selon les cas, j'hésiterais
entre cette solution et celle fournissant deux constructeurs
surchargés. Cela dépend des avantages et inconvénients.

S'il s'agit d'un constructeur très simple en dehors de l'itération,
et qu'il est appelé très fréquemment, je ne voudrais peut-être pas
m'encombrer d'une construction de vector<> à chaque appel. Par
exemple.

En tous cas, la solution avec la valeur par défaut n'est pas à
rejeter.

J'aimerais connaître les raisons exactes invoquées par la personne
dont tu parlais.

--drkm


Avatar
drkm
writes:

korchkidu wrote:

James Kanze wrote:

La question alors est : qu'est-ce que tu fais avec ce vector
dans ton constructeur ?


C'est un vecteur de pointeurs que j'itere pour initialiser mes
donnees.

Parce qu'il serait bien détruit à la fin de l'expression qui
invoque le constructeur. Alors, si tu en as fait une copie,
aucun problème.


C'est des pointeurs sur des objets qui ne sont jamais detruits.


D'accord. Le seul problème que ton exemple pourrait posé, c'est
un problème de durée de vie. Sinon, ça doit marcher sans
problème.


De quel problème de durée de vie parles-tu ? Le problème est de
savoir si initialiser le paramètre référence avec un temporaire comme
valeur par défaut peut déboucher sur quelque problème. Je ne vois pas
de problème de durée de vie à cet égard dans son exemple.

--drkm



Avatar
drkm
Marc Boyer writes:

Vladimir Votiakov wrote:

J'ai envie d'appeler ce genre de code de l'abus de intelligence
de compilateur.


Non, on transfère du coût de développement sur du coût CPU.


Et même, je ne suis vraiment pas sûr d'un tel impact sur le temps
d'exécution. Ce que proposait Vladimir, si je me souviens bien, est
quelque chose comme :

ctor() {
vector<T> v ;
init( v ) ;
}

ctor( vector<T> const & v ) {
init( v ) ;
}

void init( vector<T> const & v ) {
}

vs. :

ctor( vector<T> const & v = vector<T>() ) {
}

Je ne vois pas d'impact clair sur le CPU.

--drkm


Avatar
drkm
Olivier Azeau writes:

Dans le cas présent, si on a besoin de créer 3 itérateurs sur le
vecteur, il y a fort à parier que ni la méthode init( T const & t ), ni
la valeur par défaut ne sont justifiés.


Ça, on n'en sait rien.

Le PO est venu avec deux alternatives. Je n'ai aucune raison de
penser qu'il s'est planté dans son application, qu'il connait mieux
que moi. Il semble avoir besoin du vecteur dans les deux cas, puisque
dans les deux alternatives, s'il ne le reçoit pas, il le crée.

La question est de savoir si l'alternative avec la valeur par défaut
peut déboucher sur des problèmes. Je n'en vois pas.

Maintenant, s'il ne se sert du vecteur que pour itérer, il se peut
qu'il soit plus simple de ne pas tenter d'itérer sur un vecteur vide
lorsqu'il n'en reçoit pas. Mais ce n'est pas sûr. Que dire de :

ctor( vector<T> const & v = vector<T>() ) {
// du code devant être fait avant l'itération
for ( vector<T>::const_iterator it = v.begin() , end = v.end() ;
it != end ;
++ it ) {
// ...
}
// du code devant être fait après l'itération
}

vs. :

ctor() {
init_before_iteration() ;
init_after_iteration() ;
}

ctor( vector<T> const & v = vector<T>() ) {
init_before_iteration() ;
for ( vector<T>::const_iterator it = v.begin() , end = v.end() ;
it != end ;
++ it ) {
// ...
}
init_after_iteration() ;
}

void init_before_iteration() {
// du code devant être fait avant l'itération
}

void init_after_iteration() {
// du code devant être fait après l'itération
}

?

--drkm

Avatar
drkm
James Kanze writes:

drkm wrote:

S'il s'agit d'un constructeur très simple en dehors de
l'itération, et qu'il est appelé très fréquemment, je ne
voudrais peut-être pas m'encombrer d'une construction de
vector<> à chaque appel. Par exemple.


Est-ce que tu as une idée du coût de la construction d'un vector
vide ? Ça doit se résumer à l'initialisation de deux ou de trois
variables de base à 0. Pas grand chose, en somme.


Exactement. C'est pour cela que j'insistais sur « appelé
fréquemment », et sur le conditionnel. Le choix peut se poser, mais
il faut d'autres arguments que « une valeur par défaut pour cet
argument, c'est mal ». Le profiler peut éventuellement être un de ces
arguments.

En tous cas, la solution avec la valeur par défaut n'est pas
à rejeter.

J'aimerais connaître les raisons exactes invoquées par la
personne dont tu parlais.


Voilà la vraie question. Parce qu'à part un problème potentiel
de durée de vie (dont korchkidu semble être au courant), je n'en
vois pas.


Encore ce problème de durée de vie ... À quoi penses-tu ? Je ne
vois vraiment pas de tel problème dans le contexte de la discussion.

--drkm


Avatar
drkm
drkm writes:

Encore ce problème de durée de vie ... À quoi penses-tu ? Je ne
vois vraiment pas de tel problème dans le contexte de la discussion.


Ok, j'ai trouvé la réponse dans un autre message.

--drkm

Avatar
Vladimir Votiakov
Pas accord. Si on veut du développement simplifié on n'utilise pas
ce genre de langage (il faut aller vers java). Pour moi C++ égal
efficacité (pas à tout prix bien sur). Regardez QT 4.0, par exemple,
une des nouveautés c'est grand coup de balai sur le code inutile
(en terme de cpu justement) et en startup surtout.

Le code raisonnable pour moi :

T(): unAttribut(0), unAutre(0) {}

et

T(int i): unAttribut(i), unAutre(0) {}

et autre

T(int i, const std::vector<double>& v)
{
// blah-blah
}

Y a pas grand chose à documenter ici.

"Marc Boyer" wrote in message
news:crg85p$9b4$
Vladimir Votiakov wrote:
J'ai envie d'appeler ce genre de code de l'abus de intelligence
de compilateur.


Non, on transfère du coût de développement sur du coût CPU.
Ca me paraît une démarche constante dans l'histoire de l'informatique
et raisonnable en plus.

A quoi ça sert de créer un objet sur stack pour le balancer aussitôt ?


Eviter de passer du temps à écrire les deux constructeurs qui ne
sont qu'une coquille vide, éviter d'avoir à passer du temps
à documenter ce fouillis, éviter de faire perdre du temps
à ce qui liront la doc de la classe dans 8 mois, etc.

A mon avis, il est plus logique d'avoir 2 constructeurs
sans paramètre et avec le paramètre et si tu veut
partager du code entre les deux, une petite fonction
(inline) initialize(), par exemple.


Sauf que cela force soit à de la redondance de code, soit
à des initialisation inutiles pour les autres attributs.
Si on prend un système du genre:

F(int i, std::vector<double> v = std::vector<double>() ):
unAttribut(i), unAutre(v) {
// code de construction en rab
}

vs

F(int i, std::vector<double> v = std::vector<double>() ) {
init(i,v);
}

de toute façon, on paye la construction de unAttribut et
unAutre avec les valeurs par défaut, qu'ensuite on modifiera
dans init.

Marc Boyer
--
Je ne respecte plus le code de la route à vélo depuis une double fracture
due au fait que j'étais le seul à le respecter.



Avatar
drkm
"Vladimir Votiakov" writes:

Si on veut du développement simplifié on n'utilise pas
ce genre de langage (il faut aller vers java). Pour moi C++ égal
efficacité


C'est le premier de l'année ?

--drkm

Avatar
Olivier Azeau
wrote:
Olivier Azeau wrote:
J'aurais plutôt dit : à quoi sert de créer un objet temporaire
si on ne va pas s'en servir ? Dans le cas présent, je ne vois
pas à quoi va bien pouvoir servir le vecteur vide.



A priori, il va s'en servir. Si j'ai bien compris, le but, c'est
que l'utilisateur passe 0-n paramètre d'un type donné, et qu'on
ne connaît pas forcement le nombre de paramètres lors de la
compilation.

Le paramèter par défaut, ce n'est qu'une commodité pour
l'utilisateur, pour qu'il n'ait pas besoin de créer lui-même un
vecteur vide dans le cas où le nombre de paramètres est 0.



Ce qui est en soi, dans ce cas, une forme d'obfuscation : pourquoi
particulariser ce cas ?
Si l'intention de l'utilisateur est de passer zéro paramètres, autant le
forcer à la faire explicitement et non pas le laisser compter sur
l'utilisation d'un paramètre par défaut qui pourra évoluer.

Dans le cas présent, je ne vois pas l'utilité. Ça me
semblerait presque de l'obfuscation, dans le cas présent. On
parle bien de quelque chose comme :




void f( T const & t = T() ) {
// ...
}




vs. :




void f() {
T t ;
init( t ) ;
}




void f( T const & t ) {
init( t ) ;
}




void init( T const & t ) {
// ...
}




?




Ce qui manque dans ton exemple, c'est le contenu de la méthode
"init" : A-t-on vraiment besoin d'écrire un code générique
pour les 2 cas de surcharge, surtout si T est un vecteur et
que l'on ne va probablement pas faire le même chose quand le
vecteur est vide ?



Pourquoi est-ce qu'on ferait quelque chose de différent si le
vector est vide ? Ne connaissant pas l'application, c'est
difficile de dire plus, mais a priori, je ne vois pas de
problèmes, ni au niveau du C++, ni au niveau de la conception.



Certes, mais j'ai surtout dit ça pour mettre en évidence que tout n'est
jamais tout blanc ou tout noir : il existe autant de justifications de
faire d'une manière ou d'une autre et, effectivement, sans en savoir
plus sur l'application, il est difficile de trancher.

D'ailleurs, à mon avis, avant de se poser la question de savoir si on
peut mettre une valeur par défaut sur ce paramètre de type vecteur, la
bonne question est de savoir s'il est justifié de mettre directement un
vecteur en paramètre et si une classe qui donnerait une meilleure
sémantique à ce paramètre ne serait pas justifiée.



1 2 3 4 5